Fix heap update operation for least-loaded picker #61
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.
@jchadwick-buf, @lrewega was trying out the least-loaded picker and it panick'ed! 😱
It turns out, the
update
operation -- which reconciles the heap with a new set of connections provided from a resolve -- was iterating from start to finish of the slice, but it would also change the length of the slice while iterating, when it decided it needed to pop an item that should no longer be present.So the panic was an out-of-bounds slice index. But the iteration is incorrect for more than just that reason: removing an item from the heap that way will also re-order items, to sift things up and down to preserve heap invariants within the slice. So to proceed iterating through the slice means we might visit the same item more than once and fail to visit some items, as their order may have changed underneath us.
So now the logic does a single pass through the slice to remove unneeded items, by simply overwriting them (and setting to nil if necessary). So at the end of the first pass, all that is left in the slice are the items in the new set of connections, compacted to the beginning of the slice (everything after is set to nil). Then we append any new connections. And at the very end, we re-heapify to restore heap invariants after all of that.
This adds a test that is hopefully pretty convincing that it all works correctly now. The test is a sequence of operations, including acquiring connections, releasing them, and updating the resolved set.