LDAPReplicationDomain.disable() sets disabled = true before disableService(), so a replay thread reads the flag of a domain which is going away before its session is stopped. enable() does not mirror it: it calls enableService() and clears disabled two statements later.
// disable()
disabled = true;
disableService();
...
// enable()
enableService();
sessionGeneration++;
disabled = false;
enableService() ends with startListenService(), so the listener can take a delivery, list it and hand it to a replay thread while disabled is still true. That thread reads the guard at the top of the replay loop, gives up on the change - and abandonReplay() returns without asking for it again, because a domain which is going away owns its session. The change stays listed, uncommitted and owned by nobody, so this domain's ServerState is held back until something else restarts the session.
The window is two statements wide and nothing in the protocol can be made to land inside it, so this is an invariant to hold rather than a bug to reproduce. Clearing the flag before enableService() closes it, and putting it back if enableService() throws keeps the other half: a domain whose session could not be started owns it the way a disabled one does.
Worth doing with the session generation of #922 rather than alone: a replay thread which survived disable() reads that flag too, and neither order of these two statements is right for it (#908).
Found while reviewing #892.
LDAPReplicationDomain.disable()setsdisabled = truebeforedisableService(), so a replay thread reads the flag of a domain which is going away before its session is stopped.enable()does not mirror it: it callsenableService()and clearsdisabledtwo statements later.enableService()ends withstartListenService(), so the listener can take a delivery, list it and hand it to a replay thread whiledisabledis still true. That thread reads the guard at the top of the replay loop, gives up on the change - andabandonReplay()returns without asking for it again, because a domain which is going away owns its session. The change stays listed, uncommitted and owned by nobody, so this domain's ServerState is held back until something else restarts the session.The window is two statements wide and nothing in the protocol can be made to land inside it, so this is an invariant to hold rather than a bug to reproduce. Clearing the flag before
enableService()closes it, and putting it back ifenableService()throws keeps the other half: a domain whose session could not be started owns it the way a disabled one does.Worth doing with the session generation of #922 rather than alone: a replay thread which survived
disable()reads that flag too, and neither order of these two statements is right for it (#908).Found while reviewing #892.