[#1080] Give back the send-window permit of an update the writer drops, and count only what it sends - #1106
Open
vharseko wants to merge 1 commit into
Conversation
…date the writer drops, and count only what it sends ServerHandler.take() charges a permit of the send window for every update it hands to the writer, before ServerWriter.isUpdateMsgFiltered() decides whether the update is sent at all. Only the peer gives permits back, for what it receives, so every dropped update cost the session a permit for good; once more than half of the window was gone the peer could no longer be sent enough to give any credit back, and the writer waited for it until the session was re-established. The drops come from the catch-up of a peer: ReplicationServerDomain.put() applies the same checks before it queues, the changelog does not. A peer replication server which connected with another generation id is sent none of its backlog, and once the two agree - a reset of the generation id, a TopologyMsg - the session goes on without being re-established, with the permits of that backlog lost. The writer now gives the permit back for every update it drops, whichever filter drops it, and counts an update as sent (sent-updates and the assured counters of the monitor entry of the handler) only when it publishes it rather than when take() returns it.
maximthomas
approved these changes
Sep 25, 2026
maximthomas
left a comment
Contributor
There was a problem hiding this comment.
praise: The permit goes back in the one place every drop passes through, and the stall is reproduced with a real peer.
ServerWriter.run()callshandler.releasePermitInSendWindow()at the top of the filtered branch (ServerWriter.java:123), so every arm ofisUpdateMsgFiltered()gives its permit back without a release perreturn true.countSentUpdate()is called only on the publish branch (ServerWriter.java:132), sosent-updatesno longer counts the dropped backlog.FilteredUpdateSendWindowTest.aPeerWhichAgreesOnTheGenerationIdAfterItsBacklogWasDroppedIsSentTheNextChangedrives a real peer replication server through the in-place generation-id change of #1080. It passes at the head, 2/2 for the class, along with both rows ofReplicationServerShutdownSyncTest#onlyAPeerWhichCanDecodeTheMessageIsToldTheReplicaWentOffline.
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 #1080
The accounting
ServerHandler.take()charges a permit of the send window for every update it hands to the writer(
:994), beforeServerWriter.isUpdateMsgFiltered()decides whether the update is sent at all.The only release is the
WindowMsgthe peer sends for what it received (updateWindow()), and adropped update never reaches the peer, so every drop cost the session a permit for good. The peer
gives its credit back in halves of its window, so once more than half of the permits are gone it
can no longer be sent enough to give any back: the writer sits in
acquirePermitInSendWindow()until the session is re-established, and nothing is logged. The replication server never probes a
peer for credit.
Where the drops come from
ReplicationServerDomain.put()applies the same status and generation id checks before it queuesan update, so the drops come from the changelog: the catch-up of a peer which connects behind reads
its whole backlog there, and the writer drops every record of it.
BAD_GEN_ID_STATUSor under a total update leaves that status on a newsession - a new handler, a new semaphore - which is why [#1029] Send a directory server only the updates it gives send-window credit for #1034 left those arms alone.
ReplicationServerDomain.resetGenerationId()only callsrsHandler.setGenerationId(), and a TopologyMsg updates the generation id of the handler inplace (
ReplicationServerHandler.java:614). A peer which connected with another generation idand a backlog of more than half a window is left, once the two agree, with a session over which
nothing can be sent.
ReplicaOfflineMsgper replica which goes offline,over a session to an older peer which is not re-established for it either.
The change
ServerWriter.run()gives the permit back -ServerHandler.releasePermitInSendWindow()- in theone branch every filter goes through, rather than at each
return true.take()counted the update as sent as well -sent-updatesand the assured counters of themonitor entry of the handler - before the writer dropped it. The count moves to
ServerHandler.countSentUpdate(), which the writer calls only for what it publishes.Taking the permit only after the filter was the other option in the issue. It is not taken: the
filter reads the status of the peer when the update is written, and
take()decides the assuredflag of an update re-read from the changelog after the wait for the permit on purpose (the comment
in
take()says why), so both would have had to move with it.A writer which is shut down leaves its wait without a permit, so a release on that path may give
back one it never took. The window of a session which is going away is not used again; the javadoc
of
releasePermitInSendWindow()says so.Tests
FilteredUpdateSendWindowTest(new):aPeerWhichAgreesOnTheGenerationIdAfterItsBacklogWasDroppedIsSentTheNextChange- a real peerreplication server takes another generation id before it connects
(
ReplicationServerDomain.changeGenerationId()), so its catch-up of a whole window of backlog isdropped; it then takes the generation id of the writer over the same session (a TopologyMsg), and
the next change must reach it, with
sent-updatesat 1 and the window atW - 1. This is thestall of the issue.
aDirectoryServerInBadGenerationIdStatusKeepsItsSendWindowAndIsSentNothing- the window of adirectory server in
BAD_GEN_ID_STATUSstays whole after its catch-up, and nothing is counted assent to it.
ReplicationServerShutdownSyncTest.onlyAPeerWhichCanDecodeTheMessageIsToldTheReplicaWentOfflinenow also asserts the window and
sent-updatesof the peer, which covers the #1014 arm.sent-updates(3, 11, 2)FilteredUpdateSendWindowTest2/2,ReplicationServerShutdownSyncTest21/21,ReplicaOfflineMsgCatchUpTest2/2org/opends/server/replication/**package, JDK 11)take()fails itA test broker takes the credit of its own send window only while it receives, so the replication
server the publishing brokers talk to advertises a window of 100; the window of 10 is the one the
peer under test advertises.