https://bugs.openldap.org/show_bug.cgi?id=10026
Issue ID: 10026 Summary: Refresh handling can skip entries (si_dirty not managed properly) Product: OpenLDAP Version: unspecified Hardware: All OS: All Status: UNCONFIRMED Keywords: needs_review Severity: normal Priority: --- Component: overlays Assignee: bugs@openldap.org Reporter: ondra@mistotebe.net Target Milestone: ---
Take MPR plain syncrepl with 3+ providers.
When a provider's own syncrepl session transitions to persist and a it starts a new parallel session towards another host, that session always has to start as a refresh. If that refresh serves entries to us, our handling of si_dirty is not consistent: - if the existing persist session serves some of these entries to us, we can "forget" to pass the others to a newly connected consumer - same if the refresh is abandoned and we start refreshing from a different provider that might be behind what we were being served (again our consumers could suffer) - if we restart, si_dirty is forgotten and our consumers suffer even worse
We might need to be told (at the beginning of the refresh?) what the end state we're going for is, so we can keep si_dirty on until then. And somehow persist that knowledge in the DB...
https://bugs.openldap.org/show_bug.cgi?id=10026
--- Comment #1 from Ondřej Kuzník ondra@mistotebe.net --- Created attachment 954 --> https://bugs.openldap.org/attachment.cgi?id=954&action=edit Reproducer
A Python (3.10+) reproducer, requires OpenLDAP master (or 2.7) source with mod-harness compiled in (--enable-harness).
Extract the archive, create a virtual environment, install requirements, then run: TOP_SRCDIR=<path to openldap source> ./tester.py
The sequence to get started quickly (adjust "../openldap" accordingly): ``` tar xf test050.tgz cd test050 virtualenv ./venv source ./venv/bin/activate pip install -r ./requirements.txt TOP_SRCDIR=../openldap ./tester.py ```
https://bugs.openldap.org/show_bug.cgi?id=10026
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords|needs_review | Target Milestone|--- |2.5.15
https://bugs.openldap.org/show_bug.cgi?id=10026
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Assignee|bugs@openldap.org |hyc@openldap.org
https://bugs.openldap.org/show_bug.cgi?id=10026
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Target Milestone|2.5.15 |2.5.16
https://bugs.openldap.org/show_bug.cgi?id=10026
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Target Milestone|2.5.16 |2.5.17
https://bugs.openldap.org/show_bug.cgi?id=10026
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Target Milestone|2.5.17 |2.5.18
https://bugs.openldap.org/show_bug.cgi?id=10026
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Target Milestone|2.5.18 |2.5.19
https://bugs.openldap.org/show_bug.cgi?id=10026
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Target Milestone|2.5.19 |2.5.20
https://bugs.openldap.org/show_bug.cgi?id=10026
Ondřej Kuzník ondra@mistotebe.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Target Milestone|2.5.20 |2.6.11
https://bugs.openldap.org/show_bug.cgi?id=10026
--- Comment #2 from Howard Chu hyc@openldap.org --- requirements.txt is incomplete.
I had to pip install ldif and pytest, but was still unable to run this:
$ pip install pytest Collecting pytest Downloading pytest-9.0.1-py3-none-any.whl.metadata (7.6 kB) Collecting iniconfig>=1.0.1 (from pytest) Downloading iniconfig-2.3.0-py3-none-any.whl.metadata (2.5 kB) Collecting packaging>=22 (from pytest) Downloading packaging-25.0-py3-none-any.whl.metadata (3.3 kB) Collecting pluggy<2,>=1.5 (from pytest) Downloading pluggy-1.6.0-py3-none-any.whl.metadata (4.8 kB) Collecting pygments>=2.7.2 (from pytest) Downloading pygments-2.19.2-py3-none-any.whl.metadata (2.5 kB) Downloading pytest-9.0.1-py3-none-any.whl (373 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 373.7/373.7 kB 6.8 MB/s eta 0:00:00 Downloading iniconfig-2.3.0-py3-none-any.whl (7.5 kB) Downloading packaging-25.0-py3-none-any.whl (66 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 66.5/66.5 kB 14.9 MB/s eta 0:00:00 Downloading pluggy-1.6.0-py3-none-any.whl (20 kB) Downloading pygments-2.19.2-py3-none-any.whl (1.2 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.2/1.2 MB 33.8 MB/s eta 0:00:00 Installing collected packages: pygments, pluggy, packaging, iniconfig, pytest Successfully installed iniconfig-2.3.0 packaging-25.0 pluggy-1.6.0 pygments-2.19.2 pytest-9.0.1 (venv) hyc@viola:~/OD/hobj/tests/test050$ TOP_SRCDIR=../../../head ./tester.py Traceback (most recent call last): File "/home/hyc/OD/hobj/tests/test050/./tester.py", line 649, in <module> asyncio.run(main()) File "/usr/lib/python3.12/asyncio/runners.py", line 194, in run return runner.run(main) ^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/asyncio/runners.py", line 118, in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/asyncio/base_events.py", line 687, in run_until_complete return future.result() ^^^^^^^^^^^^^^^ File "/home/hyc/OD/hobj/tests/test050/./tester.py", line 507, in main server = Server(test_ldif) ^^^^^^^^^^^^^^^^^ File "/home/hyc/OD/hobj/tests/test050/./tester.py", line 147, in __init__ self.populate_db() File "/home/hyc/OD/hobj/tests/test050/./tester.py", line 151, in populate_db db = ldif.LDIFRecordList(open(self.dbname)) ^^^^^^^^^^^^^^^^^^^ AttributeError: module 'ldif' has no attribute 'LDIFRecordList'
https://bugs.openldap.org/show_bug.cgi?id=10026
Ondřej Kuzník ondra@mistotebe.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Attachment #954 is|0 |1 obsolete| |
https://bugs.openldap.org/show_bug.cgi?id=10026
--- Comment #3 from Ondřej Kuzník ondra@mistotebe.net --- Created attachment 1093 --> https://bugs.openldap.org/attachment.cgi?id=1093&action=edit Reproducer
Updated reproducer
https://bugs.openldap.org/show_bug.cgi?id=10026
--- Comment #4 from Ondřej Kuzník ondra@mistotebe.net --- Looks like I forgot to mention some libraries that naturally exist on my system. so updated it now. The way to run it is still the same: - have OpenLDAP compiled somewhere (with `--enable-mdb=yes --enable-syncprov=yes --enable-harness`), below assumes `../openldap` as the path, adjust to taste - download the archive, then: ``` tar xf test050.tgz cd test050 virtualenv ./venv source ./venv/bin/activate pip install -r ./requirements.txt TOP_SRCDIR=../openldap ./tester.py ```
If the issue is fixed it will pass, if the issue still exists it probably won't (there are multiple ways to reproduce it, it only tries to trigger one of them).
https://bugs.openldap.org/show_bug.cgi?id=10026
--- Comment #5 from Howard Chu hyc@openldap.org --- During `pip install...`
building '_ldap' extension creating build/temp.linux-x86_64-cpython-312/Modules x86_64-linux-gnu-gcc -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O2 -Wall -fPIC -DHAVE_SASL -DHAVE_TLS -DLDAPMODULE_VERSION=3.4.5 "-DLDAPMODULE_AUTHOR=python-ldap project" "-DLDAPMODULE_LICENSE=Python style" -IModules -I/home/hyc/OD/hobj/tests/test050/venv/include -I/usr/include/python3.12 -c Modules/LDAPObject.c -o build/temp.linux-x86_64-cpython-312/Modules/LDAPObject.o In file included from Modules/LDAPObject.c:3: Modules/common.h:20:2: error: #error Current python-ldap requires OpenLDAP 2.4.x 20 | #error Current python-ldap requires OpenLDAP 2.4.x | ^~~~~ error: command '/usr/bin/x86_64-linux-gnu-gcc' failed with exit code 1 [end of output]
note: This error originates from a subprocess, and is likely not a problem with pip. ERROR: Failed building wheel for python-ldap Successfully built syncmonitor pldap Failed to build python-ldap ERROR: Could not build wheels for python-ldap, which is required to install pyproject.toml-based projects
https://bugs.openldap.org/show_bug.cgi?id=10026
--- Comment #6 from Ondřej Kuzník ondra@mistotebe.net --- On Fri, Nov 21, 2025 at 12:33:57PM +0000, openldap-its@openldap.org wrote:
During `pip install...`
building '_ldap' extension creating build/temp.linux-x86_64-cpython-312/Modules x86_64-linux-gnu-gcc -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O2-Wall -fPIC -DHAVE_SASL -DHAVE_TLS -DLDAPMODULE_VERSION=3.4.5 "-DLDAPMODULE_AUTHOR=python-ldap project" "-DLDAPMODULE_LICENSE=Python style" -IModules -I/home/hyc/OD/hobj/tests/test050/venv/include -I/usr/include/python3.12 -c Modules/LDAPObject.c -o build/temp.linux-x86_64-cpython-312/Modules/LDAPObject.o In file included from Modules/LDAPObject.c:3: Modules/common.h:20:2: error: #error Current python-ldap requires OpenLDAP 2.4.x 20 | #error Current python-ldap requires OpenLDAP 2.4.x | ^~~~~ error: command '/usr/bin/x86_64-linux-gnu-gcc' failed with exit code 1 [end of output]
note: This error originates from a subprocess, and is likely not a problem with pip. ERROR: Failed building wheel for python-ldap Successfully built syncmonitor pldap Failed to build python-ldap ERROR: Could not build wheels for python-ldap, which is required to install pyproject.toml-based projects
If you `make install`-ed libldap headers from master, they will report a bogus version. Just let the module use libldap development headers from your distro and you'll be fine (or patch your OpenLDAP tree to report something useful). This bug isn't related to libldap anyway.
https://bugs.openldap.org/show_bug.cgi?id=10026
--- Comment #7 from Howard Chu hyc@openldap.org --- Now there's nothing happening, no output anywhere.
### (venv) viola:~/OD/o10026/tests/test050> ./tester.py ###
2421890 pts/8 Sl+ 0:00 python3 ./tester.py 2421907 pts/8 Sl+ 0:00 /home/hyc/OD/o10026/servers/slapd/.libs/slapd -d -1 -f /tmp/tmpxvsn9rm6/port2389/slapd.conf 2421938 pts/8 Sl+ 0:00 /home/hyc/OD/o10026/servers/slapd/.libs/slapd -d -1 -f /tmp/tmpxvsn9rm6/port3389/slapd.conf 2422044 pts/13 R+ 0:00 ps g
https://bugs.openldap.org/show_bug.cgi?id=10026
--- Comment #8 from Howard Chu hyc@openldap.org --- Also your original description says this problem occurs with 3+ providers and the test case only uses 2 slapd instances. Is that intentional?
https://bugs.openldap.org/show_bug.cgi?id=10026
--- Comment #9 from Ondřej Kuzník ondra@mistotebe.net --- On Tue, Nov 25, 2025 at 04:24:17PM +0000, openldap-its@openldap.org wrote:
Also your original description says this problem occurs with 3+ providers and the test case only uses 2 slapd instances. Is that intentional?
There is another stub provider that is embedded in the Python script that makes sure messages are received/routed in the correct order to massage the victim syncprov into a state when it starts to lie.
https://bugs.openldap.org/show_bug.cgi?id=10026
Howard Chu hyc@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Target Milestone|2.6.11 |2.6.12
https://bugs.openldap.org/show_bug.cgi?id=10026
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Target Milestone|2.6.12 |2.6.13
https://bugs.openldap.org/show_bug.cgi?id=10026
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Target Milestone|2.6.13 |2.6.14
https://bugs.openldap.org/show_bug.cgi?id=10026
--- Comment #10 from Howard Chu hyc@openldap.org --- Still had a bunch of errors setting up the test environment. After spending about an hour to get thru that, still getting zero output from tester.py.
You need to be more explicit about the steps to setup the environment.
You need to be much more explicit about the steps the test script is performing and what should be seen at each step.
You should not assume any distro-provided LDAP packages are installed on any developer's machine.
#### copying Lib/slapdtest/certs/server.pem -> build/lib.linux-x86_64-cpython-312/slapdtest/certs running build_ext building '_ldap' extension creating build/temp.linux-x86_64-cpython-312/Modules x86_64-linux-gnu-gcc -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O2 -Wall -fPIC -DHAVE_SASL -DHAVE_TLS -DLDAPMODULE_VERSION=3.4.7 "-DLDAPMODULE_AUTHOR=python-ldap project" "-DLDAPMODULE_LICENSE=Python style" -IModules -I/home/hyc/OD/o10026/tests/test050/venv/include -I/usr/include/python3.12 -c Modules/LDAPObject.c -o build/temp.linux-x86_64-cpython-312/Modules/LDAPObject.o In file included from Modules/LDAPObject.c:3: Modules/common.h:15:10: fatal error: lber.h: No such file or directory 15 | #include <lber.h> | ^~~~~~~~ compilation terminated. error: command '/usr/bin/x86_64-linux-gnu-gcc' failed with exit code 1 [end of output]
note: This error originates from a subprocess, and is likely not a problem with pip. ERROR: Failed building wheel for python-ldap Failed to build python-ldap ERROR: Could not build wheels for python-ldap, which is required to install pyproject.toml-based projects (venv) viola:~/OD/o10026/tests/test050> ####
https://bugs.openldap.org/show_bug.cgi?id=10026
--- Comment #11 from Howard Chu hyc@openldap.org --- running verbose:
(venv) viola:~/OD/o10026/tests/test050> ps ax | grep slapd 1611961 pts/4 S+ 0:00 grep slapd (venv) viola:~/OD/o10026/tests/test050> python3 tester.py -v DEBUG:__main__:Starting with /tmp/tmprxrh_foa DEBUG:__main__:Starting on localhost:1389 INFO:searchA:Search A started DEBUG:searchA:message=SyncInfoRefreshDelete(cookie=None, refreshDone=True) DEBUG:__main__:searchA is transitioning to stage=1 DEBUG:searchA:Will be waiting for cookie=rid=000,sid=003,csn=20260613163947.000000Z#000000#001#000000 INFO:pldap.server.server:connid=1 Accepted a new connection DEBUG:pldap.server.server:connid=1 msgid=1 sending BindResponse(msgid=1, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, serverSaslCreds=None) INFO:pldap.server.server:connid=2 Accepted a new connection DEBUG:pldap.server.server:connid=2 msgid=1 sending BindResponse(msgid=1, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, serverSaslCreds=None) DEBUG:SearchRequest(connid=1, msgid=2):Synccontrol cookie b'rid=001,sid=002' INFO:SearchRequest(connid=1, msgid=2):Have an initial sid=2 search with no cookie DEBUG:SearchRequest(connid=1, msgid=2):target_cookie=rid=000,sid=001,csn=20260613163947.000000Z#000000#001#000000 DEBUG:pldap.server.server:connid=1 msgid=2 sending SearchResultEntry(msgid=2, controls=[SyncStateControl(state=1, cookie=None)], dn='dc=example,dc=com') DEBUG:pldap.server.server:connid=1 msgid=2 sending SyncRefreshDelete(msgid=2, controls=[], cookie=b'rid=000,sid=001,csn=20260613163947.000000Z#000000#001#000000', refreshDone=True) DEBUG:__main__:sid=002 is transitioning to stage=2 INFO:__main__:searchA Reached stage 2 DEBUG:SearchRequest(connid=2, msgid=2):Synccontrol cookie b'rid=001,sid=003' INFO:SearchRequest(connid=2, msgid=2):Closing connection for sid=3 while they have no data DEBUG:pldap.server.server:connid=2 msgid=2 sending ExtendedResponse(msgid=0, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, name='1.3.6.1.4.1.1466.20036', value=None) DEBUG:pldap.server.server:connid=2 msgid=3 UnbindRequest processing finished INFO:pldap.server.server:connid=2 Connection closed DEBUG:searchA:message=SearchResultEntry(b'dc=example,dc=com', {b'objectClass': [b'top', b'organization', b'domainRelatedObject', b'dcObject'], b'dc': [b'example'], b'l': [b'Anytown, Michigan'], b'st': [b'Michigan'], b'o': [b'Example, Inc.', b'EX', b'Ex.'], b'description': [b'The Example, Inc. at Anytown'], b'postalAddress': [b'Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US'], b'telephoneNumber': [b'+1 313 555 1817'], b'associatedDomain': [b'example.com']}, [SyncStateControl(state=add, entryUUID='ac5ee4db-450d-409b-b6f7-a6aed1987196', cookie=b'rid=000,sid=003')], encoding='utf-8') INFO:pldap.server.server:connid=3 Accepted a new connection DEBUG:pldap.server.server:connid=3 msgid=1 sending BindResponse(msgid=1, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, serverSaslCreds=None) DEBUG:SearchRequest(connid=3, msgid=2):Synccontrol cookie b'rid=001,sid=003' INFO:SearchRequest(connid=3, msgid=2):Closing connection for sid=3 while they have no data DEBUG:pldap.server.server:connid=3 msgid=2 sending ExtendedResponse(msgid=0, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, name='1.3.6.1.4.1.1466.20036', value=None) DEBUG:pldap.server.server:connid=3 msgid=3 UnbindRequest processing finished INFO:pldap.server.server:connid=3 Connection closed INFO:pldap.server.server:connid=4 Accepted a new connection DEBUG:pldap.server.server:connid=4 msgid=1 sending BindResponse(msgid=1, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, serverSaslCreds=None) DEBUG:SearchRequest(connid=4, msgid=2):Synccontrol cookie b'rid=001,sid=003' INFO:SearchRequest(connid=4, msgid=2):Closing connection for sid=3 while they have no data DEBUG:pldap.server.server:connid=4 msgid=2 sending ExtendedResponse(msgid=0, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, name='1.3.6.1.4.1.1466.20036', value=None) DEBUG:pldap.server.server:connid=4 msgid=3 UnbindRequest processing finished INFO:pldap.server.server:connid=4 Connection closed INFO:pldap.server.server:connid=5 Accepted a new connection DEBUG:pldap.server.server:connid=5 msgid=1 sending BindResponse(msgid=1, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, serverSaslCreds=None) DEBUG:SearchRequest(connid=5, msgid=2):Synccontrol cookie b'rid=001,sid=003' INFO:SearchRequest(connid=5, msgid=2):Closing connection for sid=3 while they have no data DEBUG:pldap.server.server:connid=5 msgid=2 sending ExtendedResponse(msgid=0, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, name='1.3.6.1.4.1.1466.20036', value=None) DEBUG:pldap.server.server:connid=5 msgid=3 UnbindRequest processing finished INFO:pldap.server.server:connid=5 Connection closed INFO:pldap.server.server:connid=6 Accepted a new connection DEBUG:pldap.server.server:connid=6 msgid=1 sending BindResponse(msgid=1, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, serverSaslCreds=None) DEBUG:SearchRequest(connid=6, msgid=2):Synccontrol cookie b'rid=001,sid=003' INFO:SearchRequest(connid=6, msgid=2):Closing connection for sid=3 while they have no data DEBUG:pldap.server.server:connid=6 msgid=2 sending ExtendedResponse(msgid=0, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, name='1.3.6.1.4.1.1466.20036', value=None) DEBUG:pldap.server.server:connid=6 msgid=3 UnbindRequest processing finished INFO:pldap.server.server:connid=6 Connection closed INFO:pldap.server.server:connid=7 Accepted a new connection DEBUG:pldap.server.server:connid=7 msgid=1 sending BindResponse(msgid=1, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, serverSaslCreds=None) DEBUG:SearchRequest(connid=7, msgid=2):Synccontrol cookie b'rid=001,sid=003' INFO:SearchRequest(connid=7, msgid=2):Closing connection for sid=3 while they have no data DEBUG:pldap.server.server:connid=7 msgid=2 sending ExtendedResponse(msgid=0, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, name='1.3.6.1.4.1.1466.20036', value=None) DEBUG:pldap.server.server:connid=7 msgid=3 UnbindRequest processing finished INFO:pldap.server.server:connid=7 Connection closed INFO:pldap.server.server:connid=8 Accepted a new connection DEBUG:pldap.server.server:connid=8 msgid=1 sending BindResponse(msgid=1, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, serverSaslCreds=None) DEBUG:SearchRequest(connid=8, msgid=2):Synccontrol cookie b'rid=001,sid=003' INFO:SearchRequest(connid=8, msgid=2):Closing connection for sid=3 while they have no data DEBUG:pldap.server.server:connid=8 msgid=2 sending ExtendedResponse(msgid=0, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, name='1.3.6.1.4.1.1466.20036', value=None) DEBUG:pldap.server.server:connid=8 msgid=3 UnbindRequest processing finished INFO:pldap.server.server:connid=8 Connection closed INFO:pldap.server.server:connid=9 Accepted a new connection DEBUG:pldap.server.server:connid=9 msgid=1 sending BindResponse(msgid=1, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, serverSaslCreds=None) DEBUG:SearchRequest(connid=9, msgid=2):Synccontrol cookie b'rid=001,sid=003' INFO:SearchRequest(connid=9, msgid=2):Closing connection for sid=3 while they have no data DEBUG:pldap.server.server:connid=9 msgid=2 sending ExtendedResponse(msgid=0, controls=None, resultCode=0, matchedDN='', diagnosticMessage='', referral=None, name='1.3.6.1.4.1.1466.20036', value=None) DEBUG:pldap.server.server:connid=9 msgid=3 UnbindRequest processing finished INFO:pldap.server.server:connid=9 Connection closed ^CTraceback (most recent call last): File "/usr/lib/python3.12/asyncio/runners.py", line 118, in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/asyncio/base_events.py", line 687, in run_until_complete return future.result() ^^^^^^^^^^^^^^^ File "/home/hyc/OD/o10026/tests/test050/tester.py", line 621, in main searchA, searchB, searchC = await search_task ^^^^^^^^^^^^^^^^^ File "/home/hyc/OD/o10026/tests/test050/tester.py", line 390, in persistent_search async for messages in persistent_search: File "/home/hyc/OD/o10026/tests/test050/venv/lib/python3.12/site-packages/syncmonitor/ldap_wrapper.py", line 67, in __aiter__ done, _ = await asyncio.wait({self, next_item}, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/asyncio/tasks.py", line 464, in wait return await _wait(fs, timeout, return_when, loop) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/asyncio/tasks.py", line 550, in _wait await waiter asyncio.exceptions.CancelledError
During handling of the above exception, another exception occurred:
Traceback (most recent call last): File "/home/hyc/OD/o10026/tests/test050/tester.py", line 649, in <module> asyncio.run(main()) File "/usr/lib/python3.12/asyncio/runners.py", line 194, in run return runner.run(main) ^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/asyncio/runners.py", line 123, in run raise KeyboardInterrupt() KeyboardInterrupt
(venv) viola:~/OD/o10026/tests/test050>
https://bugs.openldap.org/show_bug.cgi?id=10026
Ondřej Kuzník ondra@mistotebe.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Attachment #1093|0 |1 is obsolete| |
--- Comment #12 from Ondřej Kuzník ondra@mistotebe.net --- Created attachment 1156 --> https://bugs.openldap.org/attachment.cgi?id=1156&action=edit Reproducer
https://bugs.openldap.org/show_bug.cgi?id=10026
--- Comment #13 from Ondřej Kuzník ondra@mistotebe.net --- On Sat, Jun 13, 2026 at 02:29:50PM +0000, openldap-its@openldap.org wrote:
Still had a bunch of errors setting up the test environment. After spending about an hour to get thru that, still getting zero output from tester.py.
You need to be more explicit about the steps to setup the environment.
It installs some python packages, namely python-ldap, so libldap development libraries need to exist, we went over that in comment #6. After that, there is no other requirement apart from python itself. If you want the convenience of a one-line virtual env setup, the equivalent of `whatprovides virtualenv` for your distro to get the suggested scripts (in this case `virtualenv`) would get you that.
You need to be much more explicit about the steps the test script is performing and what should be seen at each step.
It is doing what's described in comment #1, sets up a provider that's also a consumer replicating from 2 other servers that force it to lie to its consumers.
If you want to do it by hand, you'll have to read the script's outline (lines 537-612), the reason the script exists is that you need perfect control over the message sequencing across two syncrepl sessions to confuse syncprov and why one of the LDAP servers is written in Python to achieve this.
Or you can run test050 repeatedly and hope that you get the right conditions to happen just like our CI manages every so often. When this was reported, test050 had been stumbling across other bugs as well.
You should not assume any distro-provided LDAP packages are installed on any developer's machine.
After we went over this once, I would have assumed you wouldn't get stuck on the same thing again?
As for the failed run, are you sure there's no other slapd listening on ports 1389, 2389 or 3389?
Assuming you have a slapd running you want to debug, I have updated the setup to add a `-w`/`--wait` option so you can attach gdb to the server you're interested in, it's 3389 that this test forces to misbehave through an entirely reasonable ordering of messages.
Again, checking with a minimal clean environment with Debian Trixie (e.g. a container), then following just works 100% of the time for me. Add `--wait` and/or `--workdir <path>` when you want to interfere.
---- 8< ---- sed -i -e 's/Types: deb/Types: deb deb-src/' /etc/apt/sources.list.d/debian.sources apt update DEBIAN_FRONTEND=noninteractive apt install -y wget git libldap-dev virtualenv DEBIAN_FRONTEND=noninteractive apt build-dep -y openldap
git clone https://git.openldap.org/openldap/openldap wget 'https://bugs.openldap.org/attachment.cgi?id=1156' -O test050.tgz
pushd openldap ./configure --enable-harness --enable-modules && make depend && make popd
tar xf test050.tgz pushd test050 virtualenv ./venv source ./venv/bin/activate pip install -r ./requirements.txt TOP_SRCDIR=../openldap ./tester.py ---- 8< ----
https://bugs.openldap.org/show_bug.cgi?id=10026
Howard Chu hyc@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- See Also| |https://bugs.openldap.org/s | |how_bug.cgi?id=6710
--- Comment #14 from Howard Chu hyc@openldap.org --- The si_dirty flag was introduced in ITS#6710. Read more there for context.
https://bugs.openldap.org/show_bug.cgi?id=10026
--- Comment #15 from Howard Chu hyc@openldap.org --- (In reply to Ondřej Kuzník from comment #0)
Take MPR plain syncrepl with 3+ providers.
When a provider's own syncrepl session transitions to persist and a it starts a new parallel session towards another host, that session always has to start as a refresh. If that refresh serves entries to us, our handling of si_dirty is not consistent:
- if the existing persist session serves some of these entries to us, we can
"forget" to pass the others to a newly connected consumer
- same if the refresh is abandoned and we start refreshing from a different
provider that might be behind what we were being served (again our consumers could suffer)
- if we restart, si_dirty is forgotten and our consumers suffer even worse
We might need to be told (at the beginning of the refresh?) what the end state we're going for is, so we can keep si_dirty on until then. And somehow persist that knowledge in the DB...
Let's define what syncprov actually needs to remember, then, and maybe that will tell us what needs to be done.
Walking thru the problem description again:
Server A has a consumer talking to server B, that has entered persist. Server A has a consumer that starts talking to server C. This of course must start with a refresh. During that refresh, a number of changes may be received, with no contextCSN update, since that can only be sent when the refresh completes.
If server A's persist connection receives changes while the refresh is happening, and a new consumer connects at this time, some of the refresh changes may be lost?
Server A completes its refresh with server B and gets a contextCSN B0, when it enters persist phase. Server A starts a refresh against server C, including B0 in its cookie. So the changes it receives from C will be newer than B0.
Server A receives a change on its persist session from B, which makes its contextCSN B1.
Consumer D connects to A with a cookie that says what? Older than B0, presumably, so it will do a refresh up to B0 or B1. What happens next?
https://bugs.openldap.org/show_bug.cgi?id=10026
--- Comment #16 from Ondřej Kuzník ondra@mistotebe.net --- There are multiple ways this can go wrong, but the simplest one was encoded in the script, its setup is roughly as follows, but the script is the authority:
- the bundled server A (port 1389, sid=1) has 2 direct consumers (both are slapds, B: 2389 (sid=2) and C: 3389 (sid=3)), each of which is also a provider - Server C also replicates from B - the test also operates as a client, running 3 syncrepl sessions against server C: - searchA: starts straight away (and immediately turns into persist mode, as provider has same state (=empty)) this lets us monitor C's progress - searchB: see below, this is the session that C sends confused data (missing an entry) - searchC: at the end of the test we rerun a search to make sure the provider knows its actual contents
The script has complete control over the server A and the end clients (searchA/B/C), it uses that information to drive C's state: - lets B replicate some initial state - rejects C's syncrepl sessions until it has caught up with B's, at this point we know B->C is a live persist syncrepl session and we remember the cookie we got from the monitor (searchA) - C's syncrepl session to A is allowed to start, A sends the same entries plus some more but does not end the refresh yet(*) - we now send one entry to B and observe its new cookie - once that happens, start searchB with cookie above and let it progress - we let B advance by another entry, when monitor sees it land on C, finish the refresh in the A->C session and let searchB reach expected cookie as well - we rerun the syncrepl search again (searchC) to confirm C's state
You'll see that C accepts one of the entries but never sends it to searchB so searchB desyncs.
The script outlines other failure scenarios it does not try to trigger. They would diverge after stage 3 in the script (the point where you see the asterisk (*) above): ---- 8< ---- We enter stage 3 when sid=3 reports they got the next batch of entries from sid=1 (which is a refresh) After that we can do several things (all of which would expose issues): a) let sid=2 replicate another entry. From sid=3's view, it's a persist session, so while the entry is ignored, the cookie gets committed and si_dirty cleared (bad) and passed to its consumers b) stop sid=3's session to sid=1 while in the middle of the refresh and start it with another host. That might send entries in different order (the above was a refresh, order was never guaranteed), if a consumer connected before that, ignored entries might never be seen c) restart sid=3, si_dirty will get cleared, the refresh will get entries that get ignored and consumers might again miss them """
https://bugs.openldap.org/show_bug.cgi?id=10026
Howard Chu hyc@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Ever confirmed|0 |1 Status|UNCONFIRMED |IN_PROGRESS
--- Comment #17 from Howard Chu hyc@openldap.org --- https://git.openldap.org/openldap/openldap/-/merge_requests/894
https://bugs.openldap.org/show_bug.cgi?id=10026
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|IN_PROGRESS |RESOLVED Resolution|--- |TEST
--- Comment #18 from Quanah Gibson-Mount quanah@openldap.org --- head:
• 6546490c by Howard Chu at 2026-07-22T18:06:58+00:00 ITS#10026 syncprov: assume context is usually dirty
RE27:
• b8fd6ce2 by Howard Chu at 2026-07-22T19:03:25+00:00 ITS#10026 syncprov: assume context is usually dirty
RE26:
• 9fa25654 by Howard Chu at 2026-07-22T19:03:44+00:00 ITS#10026 syncprov: assume context is usually dirty
https://bugs.openldap.org/show_bug.cgi?id=10026
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|RESOLVED |VERIFIED Resolution|TEST |FIXED