Conversation
639c297 to
ff67b27
Compare
|
Rebased on The one conflict was the import block of The one thing worth a look is that #941 landed a test of the neighbouring road in the same class: The merge leaves the class with two ways of changing the number of replay threads - The PR description is updated to match. No review comments to answer yet - this is the rebase only. |
|
Ordinal moved: Nothing catches this on the way in. The additions land in different parts of the file, so git merges The open PRs which add to the file now hold 310-325 with nothing claimed twice:
No Java moved with it: the generated constant is the key name without its ordinal, so the rename is This branch is not one of the six, and its collision is the one which does not merely muddy a log.
|
4d1a83a to
8c8323f
Compare
|
Rebased, and the branch is mergeable again (8c8323f). Nothing of the change itself moved.
The conflict which mattered was reported nowhere#944 replaced They take the road the rest of Rebuilt on the heads of the stack, not on the copies it carriedThis branch was carrying the revisions of #958 and #985 as they were when it was written. It is Moving the The Run on this head
The description is updated to match. |
8c8323f to
dd3cafe
Compare
|
Rebased, and the branch is mergeable again (dd3cafe). Nothing of the change itself moved. Rebuilt on the heads of the stackThe branch is rebuilt on the current head of #958 - five commits now, the fifth being Moving the The conflict which mattered was reported nowhere#973 landed on They use Run on this head
The description is updated to match. |
dd3cafe to
9766ccb
Compare
maximthomas
left a comment
There was a problem hiding this comment.
praise: The give-back is done where the rule of #922 can hold, and the road it is on is measured, not asserted.
ReplayThread.run()gives back in afinally, on the thread which owns the changes, across every domain — the one place which sees them all; deleting that call turns the new case red atUpdateOperationTest.java:3506exactly as the description says (measured, 65 s).- The restart is placed after
createReplayThreads()and outside the twostatic synchronizedmethods, and the comment atMultimasterReplication.java:775-786says why; a redelivered change never waits in an empty pool. - On the OOME exit of a replay thread HEAD is strictly better than master: the parked changes end up unowned instead of owned by a dead thread, so any later restart or reconnect brings them back.
issue (blocking): The session restart this PR adds on the configuration road is pinned by no case.
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/MultimasterReplication.java:789-792, opendj-server-legacy/src/test/java/org/opends/server/replication/UpdateOperationTest.java:3504-3533
aChangeParkedByAThreadThePoolStoppedIsDeliveredAgain watches dependent-changes-size reach 0 and then redelivers by hand (domain.processUpdate(), :3527-3533), so the flag changesGivenBackByStoppedThreads and the restartSessionForChangesGivenBackByStoppedThreads() loop are observed by nothing. Measured at this head: the case passes with the whole :789-792 loop deleted (survives 1/1, 4.19 s vs 4.26 s); the control with giveBackParkedChanges() deleted from the finally fails at :3506. Same shape as #985 [2].
A session restart takes the domain through NOT_CONNECTED, which resets the replay counters (:1634-1637), while the give-back counts each CSN it hands back (LDAPReplicationDomain.java:3789): after the pool bounce the counter is 0 only if the restart ran.
setNumUpdateReplayThreads(2);
assertMonitorAttrValueEventually(baseDN, "dependent-changes-size", 0,
"a change parked by a replay thread the pool stopped must be given back");
assertMonitorAttrValueEventually(baseDN, "replayed-updates", 0,
"the session must be restarted for the changes which were given back");Pin: with the :789-792 loop deleted the counter keeps the given-back CSN's increment and the second assertion goes red; the hand redelivery below it can stay.
issue (non-blocking): restartSessionForChangesGivenBackByStoppedThreads() gates its entry on its own flag and then drains the shared one, so the failed-replay restart is not "left to the replay thread".
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java:3666-3676, :3691-3700, :3838
runRequestedSessionRestarts(false) takes sessionRestartRequested, which :3628 sets on every failed replay. The window is reachable: the static updateToReplayQueue survives stopReplayThreads(), the new pool drains it before the configuration thread reaches :789, and a new-pool thread which fails a replay there sets the flag; if the configuration thread wins the CAS at :3691, its getAndSet(false) takes both requests and restarts without waitBeforeSessionRestart() and without bumping consecutiveSessionRestarts (:3838 is under if (wait)). One skipped #889 backoff per configuration change, on a change which fails again at once; no interleaving loses a request. The javadoc at :3660-3665 and the description's "for those changes and no others" say otherwise.
Keeping the code and saying what it does is the smaller change:
* Run without the backoff: what went away is a replay thread, not the backend. A restart
* which a failed replay asked for in the meantime is run here as well, once, without the
* wait that road would have taken: the flags are shared, and one restart serves both.Or: run the give-back restart without draining sessionRestartRequested, so a pending failed-replay request keeps its backoff — a third state machine next to the two which are there.
suggestion (non-blocking): The loop over the domains has no per-domain isolation, unlike its sibling at :2668-2703.
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/ReplayThread.java:200-206, opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java:3649-3655, :3786-3791
Only an Error reaches it (list growth at RemotePendingChanges.java:625, the NOTE 318 formatting) — but on that road a throw at domain k skips k+1..n (owned by a dead thread, the master behaviour) and leaves k with sessionRestartRequested set (:3786) and changesGivenBackByStoppedThreads unset (:3653 runs after the return), so the configuration thread skips k now and at the next change.
for (LDAPReplicationDomain domain : MultimasterReplication.getDomains())
{
try
{
domain.giveBackChangesParkedByStoppingThread();
}
catch (Throwable giveBackFailure)
{
// Reported the way :2668-2703 reports its own; the next domain is still visited.
}
}And set the flag before the per-CSN report rather than after the helper returns: the release at :3776 is the mutation, the report loop is where an Error lands.
suggestion (non-blocking): On the OOME exit of a replay thread the finally releases every other domain's parked changes and nothing restarts those sessions now.
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/ReplayThread.java:102-105, :183-184, :200-206
MultimasterReplication.java:789 is the only caller of restartSessionForChangesGivenBackByStoppedThreads(), so a domain released on that road waits for its next failed replay, the next configuration change (the flag stays set), or a reconnect. Better than master, but the comment says "restarted by the thread which stopped this one" and nobody stopped it.
* The session which brings them back is restarted by the thread which stopped this
* one, once the pool it creates is up. A thread which dies of an OutOfMemoryError
* was stopped by nobody: the changes it releases wait for the next restart of their
* domain - a failed replay, a configuration change, a reconnection.suggestion (non-blocking): disable() clears sessionRestartRequested and not changesGivenBackByStoppedThreads.
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java:4564, :4709-4715
A flag left set (the OOME road above, or a disable racing the configuration thread's loop) runs one restart with wait=false at the next configuration change, on a session which was started fresh by enable(). Harmless; one line beside :4564.
sessionRestartRequested.set(false);
changesGivenBackByStoppedThreads.set(false);suggestion (non-blocking): The two resets in the finally of the new case are not nested.
opendj-server-legacy/src/test/java/org/opends/server/replication/UpdateOperationTest.java:3544-3545
A throw out of resetReplayGiveUpDelay() leaves the pool at 2 threads for every class which follows. Same shape as #985 M4.
finally
{
try
{
resetReplayGiveUpDelay();
}
finally
{
resetNumUpdateReplayThreads();
}
}nitpick (non-blocking): The comment on runRequestedSessionRestarts() names two readers; the configuration thread is a third.
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java:3714-3728
"Its two readers are the roads out of a failed and of an abandoned replay" and "ends the replay thread it is met on … the uncaught exception handler of DirectoryThread" are false for the reader at :3674: a throw out of restartSession() there ends the modify with an error to the LDAP client (ConfigurationHandler.java:653 has no catch). The request itself survives — the finally at :3703-3729 puts it back.
9766ccb to
bef15b3
Compare
|
Rebuilt on #985 as it stands after its round - fda30af, on master at 6dc8f80 - and then the round; the branch is that PR's The restart is pinnedThe case reads One thing added to what was proposed: the case applies a change on another entry before the bounce and asserts the count is above zero then. Without it the count before the bounce is whatever the refused duplicates of Measured, one JVM per run: the case with the The shared flagThe code stays and the text says what it does. The loop over the domainsEvery domain gets its turn: the give-back of each is under a The flag is set next to The OOME exit,
|
…wound parked as dependencies A change which waits for another one is parked and stays owned by the replay thread which parked it: getNextUpdate() is what hands it out again, to whichever thread clears the change it was waiting for. A replay which is unwound leaves that thread without the road back - it takes the next delivery off the shared queue - so the change stayed owned by a thread which never came back to it, while every redelivery of a change a replay thread owns is refused as a duplicate. On a domain which then went quiet that change was where this replica's ServerState, and every change behind it from every master, stopped. RemotePendingChanges gives back the changes the calling thread parked and unparks them in the same step, under both locks, so that only one road can hand a change out: a change released while it is still listed as waiting would be handed to the thread getNextUpdate() gives it to and to the thread which takes over the delivery which follows. The changes another thread parked are left alone, as everywhere else. The list of what it released is the one allocation of the method, sized for every change which is waiting before anything is taken out of the set - the rule getNextUpdate() states for itself, on a road out of a JVM which has just refused an allocation. replay() gives them back before the road of the change it was replaying runs, since that road restarts the session and a change which is still owned when the replication server sends it again is turned down. They are handed back without a failure counted against them - they were never applied here - and the session is restarted for them, without the backoff on the roads the owned road skips it on: a thread which is stopping, and one an OutOfMemoryError is ending. On a domain which is going away or is being imported into they are released and nothing more, the way abandonReplay() hands a change back there. Which change this thread owns is read before they are given back, and not after. The read is a plain map lookup which allocates nothing, and the give-back below it allocates - it builds the list of what it released and the line which reports each one. A throw from it on the road it exists for would otherwise reach the last resort of replay() with nothing read, and the change this thread was replaying would be left listed, uncommitted and owned by a thread which is ending: the wedge OpenIdentityPlatform#922 is about, one road over. That last resort asks for the restart as its first line, whichever road threw, and runs it outside the guard on the change this thread owned: the give-back of the parked changes asks for the same restart once it has released them, and a throw before that may have left changes nobody owns on a thread which owned no change of its own. The end-to-end test unwinds the replay of a change which is applied and whose ack runs out of memory: the one road which leaves replay() with no change of this thread's to ask for again, so the restart the give-back asks for is the only one and the test is red without it. The change it parks travels the replication server, and nothing but the restarted session brings it back.
…pool stopped had parked A change which waits for another one is parked and stays owned by the replay thread which parked it, while that thread goes back to the pool: getNextUpdate() is what hands it out again, to whichever thread clears the change it was waiting for. Changing the number of replay threads stops the whole pool and creates another one, so a thread which parked a change and went back to the queue was joined while it was idle and left recorded as the owner of that change - a thread which does not exist anymore, while every redelivery of a change a replay thread owns is refused as a duplicate. On a domain which then went quiet that change was where this replica's ServerState, and every change behind it from every master, stopped. The give-back of OpenIdentityPlatform#954 lives in the catch of replay() and no throw is on that road: the idle thread is not inside replay() at all, and the one which meets the shutdown flag returns from it normally. A replay thread now gives back what it parked on its way out, in every domain of this server: it is done by the thread which owns those changes, so the rule every road which reads ownership follows - a change is given back by the thread it was handed to and by nobody else (OpenIdentityPlatform#922) - holds here as well, and it is the one place which sees them all, since the pool is shared by every domain while a replay knows only the one it was replaying for. Every domain gets its turn whatever one of them threw - what can throw there is an allocation, on the way out of a thread an OutOfMemoryError may be ending - and the first failure is thrown once the loop is over, for the uncaught exception handler of DirectoryThread to report. The session which brings them back is restarted by the thread which stopped the pool, once the new one is up, so that a change delivered again has a replay thread to be given to. It is run only when a stopping thread gave something back - the domain records that next to the request itself, before the changes are reported - and without the backoff, what went away being a replay thread and not the backend. The request is the one flag every road shares, so a restart a failed replay asked for in the meantime is run there too, once, without its wait; a restart nobody but a failed replay asked for is left to the replay thread which asked for it, which sits through the backoff this domain has reached (OpenIdentityPlatform#889). A thread which an OutOfMemoryError is ending was stopped by nobody: what it gives back in the domains it was not replaying for waits for the next restart of their domain. disable() clears the record along with the request. The end-to-end case pins the restart as well as the give-back: replayed-updates, put above zero by a change applied before the pool is stopped, is back at zero only if the session was restarted, since nothing is delivered over the one which is started. Fixes OpenIdentityPlatform#986
bef15b3 to
d9875ed
Compare
|
Rebuilt on master at The flag of this branch sits next to the request
The restart the configuration thread runs carries the wait of what it takes
What #981 took awayThe paragraph on what a request left standing buys, and its readers - the one your nitpick was on - is gone with the flag: the checkpointer runs what stands, so the bound is not an argument anymore, and the comment there is #981's. The field's javadoc and the description say that the changes an Runs
The description is updated to match: the section on the restart, the OOME exit, the ordinals - 325 is master's - and a section on this rebase. |
maximthomas
left a comment
There was a problem hiding this comment.
praise: The restart on the configuration road is now pinned by what only a restart produces, and the round-1 loop is isolated the way it was asked.
replayed-updates > 0before the bounce,== 0after it: with theMultimasterReplication.java:789-792loop deleted the case goes red atUpdateOperationTest.java:3579— "expected [0] but found [2]", 1/1 here as well.ReplayThread.java:205-247— per-domaincatch (Throwable), first failure kept, self-suppression guarded, rethrown after the loop.changesGivenBackByStoppedThreadsis set insidegiveBackParkedChanges(boolean byStoppingThread)next to the request, so the "going away" road records nothing;disable()clears it; the fixture resets are nested.- The javadoc of
restartSessionForChangesGivenBackByStoppedThreads()now says what the code does when a failed replay's request is pending.
issue (non-blocking): The restart loop in applyConfigurationChange() has no per-domain isolation, unlike the give-back loop this round added.
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/MultimasterReplication.java:789-792
A Throwable at domain k skips k+1..n (their flags stand until the next bounce) and escapes replaceEntry after the entry, config.ldif and the bounce are done — the client gets a server error for a modify which was applied. The only Throwable off restartSession(false) on this thread is an Error (every checked and runtime failure in disableService()/enableService()/ReplicationBroker.start()/stop() is caught), so this is the same road ReplayThread.java:205-247 now isolates.
Throwable failure = null;
for (LDAPReplicationDomain domain : domains.values())
{
try
{
domain.restartSessionForChangesGivenBackByStoppedThreads();
}
catch (Throwable restartFailure)
{
if (failure == null)
{
failure = restartFailure;
}
else if (failure != restartFailure)
{
failure.addSuppressed(restartFailure);
}
}
}
if (failure instanceof Error)
{
throw (Error) failure;
}
if (failure instanceof RuntimeException)
{
throw (RuntimeException) failure;
}Or: one helper taking the per-domain action, called by both loops.
issue (non-blocking): The give-back flag is consumed before the restart runs, and nothing puts it back when the restart throws.
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java:3847, :3904-3906
compareAndSet(true, false) precedes runRequestedSessionRestarts(false); the finally at :3908 restores sessionRestartRequested only, and the flag has no other true-setter than :3988. For that domain the new comment "…or the next configuration change" is false: the next bounce reaches :3847 with the flag down and runs nothing; what is left is a failed replay in that domain (with its backoff), disable()/enable(), or a later bounce parking there again. Error road only, same as above.
if (changesGivenBackByStoppedThreads.compareAndSet(true, false))
{
boolean ran = false;
try
{
runRequestedSessionRestarts(false);
ran = true;
}
finally
{
if (!ran)
{
changesGivenBackByStoppedThreads.set(true);
}
}
}Or: make the comment at :3904-3906 say the next configuration change runs it only for the domains the loop did not reach.
suggestion (non-blocking): The pin sees that a restart ran, not that it ran without the backoff.
opendj-server-legacy/src/test/java/org/opends/server/replication/UpdateOperationTest.java:3579-3580, LDAPReplicationDomain.java:3849
runRequestedSessionRestarts(false) → runRequestedSessionRestarts(true) survives 1/1: the case takes 6.19 s instead of 4.23 s (the backoff, taken and unobserved) and stays green. Nothing in the case reads anything wait changes, so "without the backoff: what went away is a replay thread, not the backend" is enforced by no test.
Pin: assert what only wait=false leaves between :3572 and :3580 — consecutiveSessionRestarts unchanged (a package-private getter) or no #889 backoff line in logs/errors. A bound on the case time is not a pin.
suggestion (non-blocking): enable() clears neither flag, so a give-back which races disable() runs one no-backoff restart after the domain comes back.
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java:3970, :4830-4874, :4989-5038
The gate shutdown.get() || disabled is read with no lock; disable() clears both flags under serviceStateLock; a stopping thread past the gate sets request and flag after that clear, remotePendingChanges.clear() at :4869 has already forgotten what it gave back, and the next bounce after enable() restarts a session for nothing. Harmless, two lines.
// enable(), next to disabled = false
sessionRestartRequested.set(false);
changesGivenBackByStoppedThreads.set(false);nitpick (if-minor): "Run here as well, once, without the wait that road would have taken" holds only when this thread takes the recovery first.
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java:3835-3843, :3866, :4053
When the failed replay's thread already holds replayFailureRecovery and sleeps in waitBeforeSessionRestart() outside serviceStateLock, the :3866 CAS fails, this method returns without running anything, that thread's enableService() resends the given-back changes, and its inner re-read at :3870 then runs a second restartSession(true) for the give-back request. Nothing is lost; the changes wait for that backoff and one redundant bounce follows.
* A restart which a failed replay asked for in the meantime is run here as well, once,
* without the wait, when this thread takes the recovery first. When that replay's restart
* is already under way, these changes ride on it: its resend carries them, and the
* request it finds afterwards costs one more restart, with the backoff.
nitpick (if-minor): The comment names the list and the report as the only allocations on the OOME road; the iterator over the domains comes first.
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/ReplayThread.java:207, :216-222
for (… : MultimasterReplication.getDomains()) obtains an iterator over the live domains.values() before the first per-domain try; an allocation refused there releases nothing in any other domain and sets no flag. Multi-domain only — the domain of the failed replay is released at LDAPReplicationDomain.java:2709 before the rethrow. The allocation has no cheaper form; name it.
A change which waits for another one is parked -
RemotePendingChanges.checkDependencies()puts it independentChanges- and stays owned by the replay thread which parked it while that thread goes back to the pool and takes the changes which follow.getNextUpdate()is what hands it out again, to whichever replay thread clears the change it was waiting for, and that thread takes it over: the ownership of a parked change is deliberately held by a thread which is not replaying it right now.That holds for as long as the parking thread comes back to the pool. It does not hold when the pool is stopped.
MultimasterReplication.applyConfigurationChange()callsstopReplayThreads(), which sets the shutdown flag on everyReplayThreadand joins them, and thencreateReplayThreads()makes a new pool. A thread which parked a change and came back to the queue is idle when it is stopped, and it ended still recorded as the owner of that change - a thread which does not exist anymore - whileputRemoteUpdate()refuses every redelivery of a change a replay thread owns. On a domain which then goes quiet, that change is where this replica's ServerState, and every change behind it from every master, stops.The trigger is wider than the number of replay threads:
applyConfigurationChange()is the change listener of the wholecn=Multimaster Synchronizationentry and stops the pool before it looks at what changed, so a modification ofds-cfg-connection-timeoutdoes it too.Where the give-back of #954 does not reach
It lives in the
catchofreplay(), and neither road out of a stopped pool throws:replay()at all - it parked a change, met agetNextUpdate()which returned nothing, and returned to the queue;replayThreadShutdownat the top of the replay loop and leaves byabandonReplay(), which returns fromreplay()normally: what it was replaying is handed back, what it parked is not.The thread gives back what it parked
ReplayThread.run()gives back, in afinally, the changes this thread parked - in every domain of this server, throughLDAPReplicationDomain.giveBackChangesParkedByStoppingThread(). Done by the thread which owns them rather than by whoever stopped it, so the rule every road which reads ownership follows holds here as well: a change is given back by the thread it was handed to and by nobody else (#922). Doing it there is also what sees them all - the pool is shared by every domain of this server, and a thread may have parked a change in any of them, while areplay()knows only the domain it was replaying for. It covers the abandoned-replay road at the same time, so that road needs no give-back of its own.Every domain gets its turn whatever one of them threw. What can throw on that road is an allocation, on the way out of a thread an
OutOfMemoryErrormay be ending - the list of what a domain released, made before anything is released, or the report of a change once it is - and a throw at one domain would otherwise leave the ones after it with changes owned by a thread which does not exist anymore, the state this give-back is for. The first failure is thrown once the loop is over, for the uncaught exception handler ofDirectoryThreadto write the line and raise the alert; no message is added for it. A domain records that it is waiting for the restart next to the request itself, before its changes are reported, so a report which can not be formatted does not leave a request standing with nobody told to run it.The loop of
run()moves intoreplayUntilStopped()so that thefinallydoes not re-indent it.The restart is run for those changes
A change which nobody owns is one only a new delivery brings back, so the session has to be restarted for it. It is run by the thread which stopped the pool, from
applyConfigurationChange(), once the new pool is up - a change delivered again while the pool is empty would wait in the replay queue rather than be replayed - and last in that method, so the sessions which start run on the configuration this change carries. A thread on its way out is not held for a session either.It is run only when a stopping thread actually gave something back -
restartSessionForChangesGivenBackByStoppedThreads()acts on a flag of its own. The give-back asks for the restart without the backoff,SessionRestart.NOW- since #981 the wait belongs to the request rather than to the thread which runs it: what went away is a replay thread, not the backend, and these changes were never applied here. The request it runs is the one every road shares, so a restart which a failed replay asked for in the meantime - a change the new pool took off the replay queue and could not apply before the configuration thread got there - is run there as well, once, and with the wait that request was made with:SessionRestartRequestskeeps the one which asks for more, so no request is lost, none is answered by less than it asked for, and one restart serves both. What is not run from there is a restart nobody but a failed replay asked for: that one is left to the replay thread which asked for it, which sits through the backoff this domain has reached on the change it can not apply (#889), or to the state checkpointer.A thread which an
OutOfMemoryErroris ending was stopped by nobody. The change it was replaying, and what it had parked in that same domain, are given back and asked for again on its way out ofreplay()- the road of #985 - while what it gives back here, in the domains it was not replaying for, is asked for and left standing: the state checkpointer of #981 runs it within its next tick, or whichever road asks for a restart first does.disable()clears the flag along with the request: the changes are gone with the pending list, andenable()starts the session which would have been restarted.The changes handed back stay listed and uncommitted, and stay among the changes the newer ones are checked against, the way a change whose replay failed does. They are handed back without a failure counted against them - they were never applied here - as on the road #954 takes. Message 318 is generalised: it is now the report of a parked change whose replay thread went away, whichever way it went.
Tests
UpdateOperationTest.aChangeParkedByAThreadThePoolStoppedIsDeliveredAgain: one replay thread, a change whose replay fails is the barrier, the change which follows it on the same entry is parked behind it by that thread, a change on another entry is applied - so thatreplayed-updates, the count of the deliveries the session took off, is above zero for a reason other than the give-back - andds-cfg-num-update-replay-threadsis then changed: the pool is stopped and created again, the way an administrator has it. It watchesdependent-changes-sizego back to zero, which is the give-back, andreplayed-updatesgo back to zero, which is the restart: a session which is started counts from zero, and nothing is delivered over the one which is started, since the changes of this test never travelled a session. Then it has the changes delivered again by hand - nothing else sends them - and checks that the change which was parked is applied.Measured on this head, one JVM per run: with the loop over
restartSessionForChangesGivenBackByStoppedThreads()deleted fromapplyConfigurationChange()the case is red on the second reading,the session must be restarted for the changes which were given back expected [0] but found [2]; withgiveBackParkedChanges()deleted from thefinallyofReplayThread.run()it is red on the first,a change parked by a replay thread the pool stopped must be given back expected [0] but found [1].Run locally on this head, one JVM per class:
UpdateOperationTest33/33,RemotePendingChangesTest24/24,AssuredReplicationPluginTest14/14,IsServerFailureTest23/23,ModifyConflictTest36/36,NamingConflictTest21/21,PendingChangesTest9/9,DependencyTest3/3,StateMachineTest5/5 - 168 tests,Skipped: 0.On master with #981
#981 is merged (
c6b483127f), and the stack is rebuilt on it: the[#954]commit as #985 has it on that master (42284c541b), and the[#986]commit over it. What that rebase had to decide is what #985's had to: the flag the give-back set issessionRestarts.request()now, sogiveBackParkedChanges(SessionRestart, boolean byStoppingThread)takes what the restart is asked for as and records, next to the request, that the configuration thread is to run it;restartSessionForChangesGivenBackByStoppedThreads()runsrunRequestedSessionRestarts(), which takes no wait anymore. The comment on a request left standing is #981's: the paragraph which bounded what such a request buys, and named its readers, is gone with the flag - the state checkpointer runs what stands, so the bound is not an argument anymore.disable()clears the flag next tosessionRestarts.clear().Rebuilt on #985 as it stands after its round
#958 is in master (776339a). The branch is rebuilt on the head of #985 as it stands after its round of review - fda30af, one
[#954]commit on master at 6dc8f80 - and the[#986]commit goes over it with one conflict, in prose: the javadoc ofgiveBackParkedChanges(), where that round added the paragraph on a domain which is going away and this branch its own on the thread which is stopping. It carries both.What that round changed under this branch:
giveBackParkedChanges()releases and does nothing more on a domain which is going away or is being imported into - no request, no count, no line - and this branch now records that a restart is to be run next to the request, so that road records nothing either.The earlier rounds of this rebase stand. The barrier of the test is
ModifyMsgWhoseOperationRefusesAControl- an operation which is built and fails before the CSN of the change is read, so the change is kept out of the ServerState and asked for again - since #973 has a modify whose entry DN does not parse stepped over rather than asked for again. The test shortens thereplay-give-up-delaybudget throughsetReplayGiveUpDelay(TEST_GIVE_UP_DELAY), in the middle of the test rather than at its head, so that the phase which parks a change behind one whose replay keeps failing runs on the default budget; the reset is in thefinally, nested with the reset of the number of replay threads. The class carries two ways of changing the number of replay threads:setNumberOfReplayThreads(), which #941 added and which applies a mocked configuration so that it can be run from a thread of its own, andsetNumUpdateReplayThreads(), which modifiesds-cfg-num-update-replay-threadson the configuration entry the way an administrator does. They are left as they are: what each test needs of that road is not the same.Stacked on #985
This is written on top of #985, which is where the give-back of a parked change lives. Two commits: that PR's
[#954]and the[#986]which is new here. Once #985 is merged this PR is that one commit.Fixes #986
Ordinal
NOTE_REPLAY_PARKED_CHANGE_GIVEN_BACKis 318, which is where #985 has it: the message comes in on that PR's commit, and this branch only generalises its text. Master holds 310-317 (315-317 with #958) and 319-327 (325 with #981). The open PRs which add toreplication.propertieshold the rest with nothing claimed twice:318#985, #988 ·328#1019 ·329#1044 ·330#1045 ·331#1049.