[#1029] Send a directory server only the updates it gives send-window credit for - #1034
Open
vharseko wants to merge 1 commit into
Conversation
…it gives send-window credit for The replication server takes a permit of the session's send window for every message it hands to the writer (ServerHandler.take()), and a directory server gives credit only for the updates it replays: its listener calls processUpdateDone() - the one road to updateWindowAfterReplay() - for the messages which contribute to the domain state alone. A ReplicaOfflineMsg does not, so one sent to a directory server cost the session a permit for good. ReplicationServerDomain.put() never queues that message for a directory server, but the catch-up path reads the changelog, where the cursor of a replica which went offline synthesizes one from its offline CSN, and MessageHandler.updateServerState() does not move the state of the handler past an offline CSN, so every catch-up round - a hundred records at most - read the same message again, one permit each. A directory server catching up some five thousand changes past one offline replica lost more than half its window; the directory server then never accumulated the fifty replays which earn a WindowMsg, the semaphore stayed at zero and the writer sat in acquirePermitInSendWindow() until the session was closed. Nothing was logged. DataServerHandler now overrides updateServerState() to drop any update which does not contribute to the domain state before getNextMessage() returns it - the gate all three of its return paths go through, and the one place ahead of take() and its permit. The writer's own filter runs after the permit is taken, so a filter there would have leaked the same way. ReplicationServerShutdownSyncTest.theForwardToADirectoryServerDoesNotEndTheWait asserted the delivery this removes; it now asserts that a directory server is not sent the message queued for it and that the shutdown still waits. ReplicaOfflineMsgCatchUpTest pins the accounting on both catch-up branches through current-send-window on the handler: 97 and 98 on master where 98 and 99 are expected, the missing permit being the offline message each directory server was sent.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1029
The accounting
The replication server takes a permit of the session's send window for every message it hands to
the writer (
ServerHandler.take(),:991-994), and the only release is theWindowMsgthedirectory server sends (
updateWindow(),:1103). The directory server gives that credit for theupdates it replays alone: its listener calls
processUpdateDone()- the one road toupdateWindowAfterReplay()- only whencontributesToDomainState()holds(
ReplicationDomain.java:3264-3273). AReplicaOfflineMsgnever does(
ReplicaOfflineMsg.java:104-107), so one sent to a directory server costs the session a permitfor good.
ReplicationServerDomain.put()never queues that message for a directory server(
isUpdateMsgFiltered(),:498), but the catch-up path reads the changelog, where the cursor of areplica which went offline synthesizes one from its offline CSN (
FileChangelogDB.java:843-853,ReplicaCursor.java:107-131), andMessageHandler.updateServerState()(:659-665) returns!serverState.cover(csn)for it without moving the state - so every catch-up round, a hundredrecords at most (
:449), read the same message again, one permit each.Two things the issue does not say:
server sends a
WindowMsgonce fifty replays have accumulated (halfRcvWindow,ReplicationBroker.java:2708); with fewer than fifty permits left the replication server cannotsend enough to get there, the semaphore stays at zero and the writer sits in
acquirePermitInSendWindow()until the session is closed. Some five thousand changes ofcatch-up past one offline replica, fewer with large entries (a round is also capped at 50 KB).
Nothing is logged; only a reconnect - a new handler, a new semaphore - gets it moving.
offline is sent the message once, even an up-to-date one: its first round holds nothing but the
offline message, and the
!queueContributesToDomainStatebranch ofgetNextMessage()(
MessageHandler.java:329-346) returns it. Only directory-server sessions leak: a peerreplication server credits every
UpdateMsgit receives (ServerHandler.put()→decAndCheckWindow(),:1243-1246).The change
DataServerHandleroverridesupdateServerState()and returnsfalsefor any update which doesnot contribute to the domain state: the same condition the directory server credits on, so the
two sides of the accounting agree by construction. That method is the gate all three return paths
of
getNextMessage()go through, and it runs ahead oftake()and its permit. Of the threeplaces the issue lists, this is the first; the writer's own filter (
ServerWriter.java:108) isafter
take(), so a filter placed there - the letter of option 1 - would leak exactly thesame way, and option 2 would break the symmetry with a peer replication server, which credits
every message it is sent.
The comment #947 left in
ServerWriterdescribed the delivery this removes and is reworded; the!handler.isDataServer()guards stay, since they say whose forward the shutdown counts.Tests
ReplicaOfflineMsgCatchUpTest- a replica publishes a change and itsReplicaOfflineMsgandleaves; a directory server connects and catches up; a third replica publishes a change, and once
the directory server holds that one nothing is left to account for. The send window is read from
current-send-windowon the monitor entry of the handler, and the broker of the test never givescredit, so the window is the announced size less the messages sent.
aDirectoryServerBehindTheOfflineReplicaIsSentItsChangesButNotItsOfflineMessage- the roundholds the change and the offline message. On master:
current-send-window97 where 98 isexpected, the directory server received
[DeleteMsg, ReplicaOfflineMsg].aDirectoryServerUpToDateWithTheOfflineReplicaIsNotSentItsOfflineMessage- the directoryserver announces a state holding the change, so the round holds nothing but the offline
message (the other branch of
getNextMessage()). On master: 98 where 99 is expected, thedirectory server received
[ReplicaOfflineMsg].ReplicationServerShutdownSyncTest.theForwardToADirectoryServerDoesNotEndTheWait([#917] Wait for every peer replication server to forward the ReplicaOfflineMsg #947)asserted that the directory server receives the message queued for its handler, which is the
delivery this PR removes. Renamed
theDirectoryServerIsNeitherSentTheMessageNorEndsTheWait: achange is queued behind the message, the directory server receives the change and not the
message, and the shutdown still waits the grace period. Red on master (the message was received).
ReplicationTestCase.receiveUntil()- receives up to a given CSN and returns what came before,so that what a broker was not sent can be asserted without waiting out a timeout.
Locally, with the fix:
org/opends/server/replication/server/**,ReSyncTest,StateMachineTest,UpdateOperationTest- 1862 tests, no failures.Not in this PR
ServerWriter.isUpdateMsgFiltered()drops updates for a directory server inBAD_GEN_IDorFULL_UPDATEstatus aftertake()charged the permit as well. Harmless today - both statuses endin a reconnect, which brings a new handler and a new semaphore - and left alone here.
#964 (approved, open) edits the wait loop of
getNextMessage()and addsisFedByTheDomain()toDataServerHandlerjust beforeisDataServer(); the override here goes just after it. Adjacenthunks, no shared lines - this branch is cut from master, whichever lands first the other rebases
cleanly.