-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Client respect Node-Selection-Strategy
response header
#688
Conversation
UNKNOWN; | ||
|
||
static List<DialogueNodeSelectionStrategy> fromHeader(String header) { | ||
return Splitter.on(",").splitToList(header).stream() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can extract out the splitter to a static field, and include trimming whitespace: Splitter.on(',').trimResults().omitEmptyStrings()
dialogue-core/src/main/java/com/palantir/dialogue/core/DialogueNodeSelectionStrategy.java
Show resolved
Hide resolved
dialogue-core/src/main/java/com/palantir/dialogue/core/DialogueNodeSelectionStrategy.java
Outdated
Show resolved
Hide resolved
|
||
private static DialogueNodeSelectionStrategy safeValueOf(String value) { | ||
try { | ||
return valueOf(value.trim().toUpperCase()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to support mixed case strategies?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Http spec only required header names be case insensitive so its up to each implementer to decide how it interprets the values. For simplicity I figured it was fine to be completely case insensitive.
dialogue-core/src/main/java/com/palantir/dialogue/core/NodeSelectionStrategyChannel.java
Outdated
Show resolved
Hide resolved
import java.util.stream.Collectors; | ||
|
||
@SuppressWarnings("NullAway") | ||
public final class DefaultNodeSelectionStrategySelector implements NodeSelectionStrategySelector { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be helpful to mark a meter each time a strategy change occurs, possibly including the strategy we're moving to.
ticker, | ||
channelName)); | ||
|
||
nodeSelectionStrategy.updateChannels(ImmutableList.copyOf(limitedChannelByUri.values())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could keep things simpler by putting our complete trust in the most recent response we've received, that way the nodeSelectionStrategy wouldn't need to wrap individual uri channels or be notified of uri updates.
No real proposal here, just weighing implementing this in a more naive way to begin with.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya I had thought of that originally, but it would lead to pretty bad behaviour during rolling upgrades with RR/Balanced. I think the extra bit of complexity is worth handling the case
channels, random, tick, config.taggedMetricRegistry(), channelName); | ||
} | ||
throw new SafeRuntimeException( | ||
"Unknown NodeSelectionStrategy", SafeArg.of("unknown", config.nodeSelectionStrategy())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DialogueChannel is much easier to follow without the node selection logic 👍
dialogue-core/src/main/java/com/palantir/dialogue/core/NodeSelectionStrategyChannel.java
Outdated
Show resolved
Hide resolved
dialogue-core/src/main/java/com/palantir/dialogue/core/NodeSelectionStrategyChannel.java
Outdated
Show resolved
Hide resolved
dialogue-core/src/main/java/com/palantir/dialogue/core/NodeSelectionStrategyChannel.java
Show resolved
Hide resolved
dialogue-core/src/main/java/com/palantir/dialogue/core/NodeSelectionStrategySelector.java
Outdated
Show resolved
Hide resolved
dialogue-core/src/main/java/com/palantir/dialogue/core/NodeSelectionStrategyChannel.java
Outdated
Show resolved
Hide resolved
dialogue-core/src/main/java/com/palantir/dialogue/core/DialogueNodeSelectionStrategy.java
Outdated
Show resolved
Hide resolved
...-core/src/test/java/com/palantir/dialogue/core/DefaultNodeSelectionStrategySelectorTest.java
Outdated
Show resolved
Hide resolved
…ectionStrategySelector.java Co-Authored-By: Carter Kozak <ckozak@ckozak.net>
Generate changelog in
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it, thanks Felipe!
@iamdanfox Have you had a chance to read over this? This is a substantial enough change that I'd like to hold off merging until you've had a chance to review as well.
|
||
private static DialogueNodeSelectionStrategy safeValueOf(String value) { | ||
String normalizedValue = value.toUpperCase(); | ||
if (PIN_UNTIL_ERROR.name().equals(normalizedValue)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
meganit: We can avoid toUpperCase in favor of equalsIgnoreCase
* README for node selection strategy * Consistent capitalization
…ntir/dialogue into fo/server-side-node-selection
…ntir/dialogue into fo/server-side-node-selection
PIN_UNTIL_ERROR, | ||
PIN_UNTIL_ERROR_WITHOUT_RESHUFFLE, | ||
BALANCED, | ||
UNKNOWN; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can probably ditch the UNKNOWN variant here, as this library is the source of truth for what strategies exist?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But you could have an old client communicating with a new server and hence receive an unknown strategy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also can we put a little comment saying why this is copy&pasted version of the regular remoting-api NodeSelectionStrategy? (iirc this was so that we could try out implementation details without having to promote them to a full user-selectable option?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
old client communicating with a new server and hence receive an unknown strategy
in this case though we just ignore it right? i'm not sure what the unknown
variant really buys us.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It becomes important if you decide to have more complicated logic (what was previously implemented) for determining the node selection strategy to use. If you really don't like it, i'm happy to remove it
|
||
LimitedChannel channel(); | ||
|
||
ImmutableList<LimitedChannel> hostChannels(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are hostChannels saved here rather than just using an instance field? ah nvm it's live reloading again isn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya we need to keep track of the host channels so that we can recreate the underlying channel in response to a server's node-selection-strategy response
dialogue-core/src/main/java/com/palantir/dialogue/core/NodeSelectionStrategyChannel.java
Show resolved
Hide resolved
dialogue-core/src/main/java/com/palantir/dialogue/core/NodeSelectionStrategyChannel.java
Show resolved
Hide resolved
dialogue-core/src/main/java/com/palantir/dialogue/core/NodeSelectionStrategySelector.java
Outdated
Show resolved
Hide resolved
private static void setResponse(LimitedChannel mockChannel, Optional<String> header) { | ||
Mockito.clearInvocations(mockChannel); | ||
Mockito.reset(mockChannel); | ||
Response resp = mock(Response.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would prefer to use TestResponse.withheader rather than mocking, but not a big deal
void handles_one_to_many_uri_update() { | ||
channel.updateChannels(ImmutableList.of(channel1)); | ||
channel.updateChannels(ImmutableList.of(channel1, channel2)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Somewhat concerned that these tests could pretty easily vacuously pass, but maybe we have enough unit tests elsewhere that prove we can make requests correctly after a reload?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok let's give it a shot! V. excited to be able to get our Balanced implementation out to more of the fleet. I expect it's gonna be a bit weird when blue-greening between nodes that suggest different Node-Selection-Strategies, but this should be pretty transient and I guess we can watch the metrics to see when it happens :)
Released 1.32.0 |
Before this PR
Clients would declare (or default to) a static node selection strategy. Clients would have to guess which node selection strategy was the right one to use for a given service, resulting in a leaky abstraction since they would have to know implementation details of the service they were calling to choose the right one.
After this PR
==COMMIT_MSG==
Clients respect
Node-Selection-Strategy
response header==COMMIT_MSG==
Servers are now able to instruct clients on which strategy to use by adding
Node-Selection-Strategy
response header. The header is structured similarly toContent-Type
as a;
delimited list of strategies, ordered by preference. The current supported strategies are:The expectation is that for a given server a single node selection strategy will be defined.
The hope is that by consolidating node selection strategy selection into the server we will see improved perf across the fleet and clients will have to think less about the services they communicate with.
Currently the
NodeSelectionStrategySelector
decides which node selection strategy to use, basically the first known strategy from the most recent response.Possible downsides?
This raises some questions about how we handle client creation internally, since we currently create separate DialogueChannel's (and underlying Apache client) if two logic services have different static client configuration (including node selection strategy) but they will converge to the same strategy if the server responds with
Node-Selection-Strategy
header.There is also currently no way for the client to override the server suggested strategy, some may view that as a feature rather than a bug however 🤷♂️