Conversation
BlockExchangeSendingMailbox is always local, but it only delivers by reference when a mailbox of the exchange it decorates does. Reusing isLocal() for the copy decision in BroadcastExchange therefore made every spool receiver stage take a copy, including stages whose workers all serialize the block.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #19428 +/- ##
============================================
+ Coverage 67.57% 67.59% +0.02%
Complexity 1430 1430
============================================
Files 3487 3487
Lines 224228 224275 +47
Branches 35394 35406 +12
============================================
+ Hits 151511 151605 +94
+ Misses 60691 60641 -50
- Partials 12026 12029 +3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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.
PR flow
BroadcastExchange now decides block copying based on whether any destination mailbox delivers blocks by reference, avoiding unnecessary copies for spool stages with all-remote workers.
AI-generated · Green: added · Yellow: modified · Red: removed · Gray: existing
Diff evidence
Fixes #19427. Follow-up to #19353.
Problem
#19353 made
BroadcastExchangecopy blocks that carry aggregation intermediate results, because a destination can hand an on-heap block to a receiver by reference. It skips the copy for destinations that are not local, because those serialize the block withinsend.That skip never applied to spools, which are the case #19353 was written for. A multi-send (spool) node wraps each receiver stage's inner exchange as a
BlockExchange.BlockExchangeSendingMailbox, andisLocal()on that wrapper returnstrueunconditionally. Every outer destination of a spool is such a wrapper, so a receiver stage with no worker on the sending server still got a copy. The inner exchange then serialized that copy immediately, on the same thread, and no receiver ever mutated it. Each wasted copy costs one serialization plus one deserialization of every non-nullOBJECTcell.The cause is that
isLocal()answered two different questions:BlockExchange#sendBlockasks "can I pass the block whole, without splitting it?"BroadcastExchange#routeasks "can a receiver keep a reference to this block?"The two answers differ for
BlockExchangeSendingMailbox. Only the conservative direction kept the code correct.Fix
Add
SendingMailbox#deliversByReference(), and use it inBroadcastExchange#route:InMemorySendingMailboxreturnstrue.GrpcSendingMailboxreturnsfalse.BlockExchangeSendingMailboxreturnstrueif any mailbox of its inner exchange returnstrue.The answer of a mailbox never changes, so each exchange computes it once, when it is created.
The delegation is an OR over the inner mailboxes. An inner
HashExchangebuilds a new block for each destination, but those blocks hold the same cell objects, so one by-reference worker in a receiver stage is enough to require a copy.isLocal()now only means whatsendBlockneeds. This PR also moves the contract thatroutedepends on — a mailbox that does not deliver by reference must finish reading the block beforesendreturns — fromGrpcSendingMailboxtoSendingMailbox#send, where implementers can see it.Testing
BroadcastExchangeTestgets two spool cases: receiver stages whose workers are all remote (no copies), and a receiver stage with one worker on this server next to one on another server (one copy, shared within that stage). The first fails without this change.BlockExchangeTestcovers the delegation directly.WindowFunnelTestandSpoolIntegrationTeststill pass.Out of scope
The same wrapper also reports
isLocal() == truetoBlockExchange#sendBlock, while the inner exchanges getBlockSplitter.NO_OP, so multi-send blocks to remote receivers are never split againstMAX_MAILBOX_CONTENT_SIZE_BYTES. That defect is tracked separately in #19427.