- 
                Notifications
    
You must be signed in to change notification settings  - Fork 25.6k
 
Negotiate disordered transport handshakes #123397
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
Changes from 4 commits
9d4da04
              28c1cdb
              e0e015e
              66aecf9
              0ddd5aa
              184ada9
              329e511
              a42fc59
              15dd987
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| 
          
            
          
           | 
    @@ -11,6 +11,7 @@ | |
| 
     | 
||
| import org.elasticsearch.Build; | ||
| import org.elasticsearch.TransportVersion; | ||
| import org.elasticsearch.TransportVersions; | ||
| import org.elasticsearch.action.ActionListener; | ||
| import org.elasticsearch.cluster.node.DiscoveryNode; | ||
| import org.elasticsearch.common.bytes.BytesReference; | ||
| 
          
            
          
           | 
    @@ -210,11 +211,15 @@ void handleHandshake(TransportChannel channel, long requestId, StreamInput strea | |
| assert ignoreDeserializationErrors : exception; | ||
| throw exception; | ||
| } | ||
| ensureCompatibleVersion(version, handshakeRequest.transportVersion, handshakeRequest.releaseVersion, channel); | ||
| channel.sendResponse(new HandshakeResponse(this.version, Build.current().version())); | ||
| channel.sendResponse( | ||
| new HandshakeResponse( | ||
| ensureCompatibleVersion(version, handshakeRequest.transportVersion, handshakeRequest.releaseVersion, channel), | ||
| Build.current().version() | ||
| ) | ||
| ); | ||
| } | ||
| 
     | 
||
| static void ensureCompatibleVersion( | ||
| private static TransportVersion ensureCompatibleVersion( | ||
| TransportVersion localTransportVersion, | ||
| TransportVersion remoteTransportVersion, | ||
| String releaseVersion, | ||
| 
        
          
        
         | 
    @@ -224,12 +229,30 @@ static void ensureCompatibleVersion( | |
| if (remoteTransportVersion.onOrAfter(localTransportVersion)) { | ||
| // Remote is newer than us, so we will be using our transport protocol and it's up to the other end to decide whether it | ||
| // knows how to do that. | ||
| return; | ||
| return localTransportVersion; | ||
| } | ||
| if (remoteTransportVersion.isKnown()) { | ||
| // Remote is older than us, so we will be using its transport protocol, which we can only do if and only if its protocol | ||
| // version is known to us. | ||
| return; | ||
| final var bestKnownVersion = remoteTransportVersion.bestKnownVersion(); | ||
| if (bestKnownVersion.equals(TransportVersions.ZERO) == false) { | ||
| if (bestKnownVersion.equals(remoteTransportVersion) == false) { | ||
| // Remote is older than us, and we do not know its exact transport protocol version, so we choose a still-older version | ||
                
       | 
||
| // which we _do_ know (at least syntactically). However, this means that the remote is running a chronologically-newer | ||
| // release than us, even though it's a numerically-older version. We recommend not doing this, it implies an upgrade | ||
| // that goes backwards in time and therefore may regress in some way: | ||
| logger.warn( | ||
| """ | ||
| Negotiating transport handshake with remote node with version [{}/{}] received on [{}] which appears to be \ | ||
| from a chronologically-older release with a numerically-newer version compared to this node's version [{}/{}]. \ | ||
| Upgrading to a chronologically-older release may not work reliably and is not recommended. \ | ||
| Falling back to transport protocol version [{}].""", | ||
| releaseVersion, | ||
| remoteTransportVersion, | ||
| channel, | ||
| Build.current().version(), | ||
| localTransportVersion, | ||
| bestKnownVersion | ||
| ); | ||
| } // else remote is older and we _do_ know its version, so we just use that without further fuss. | ||
| return bestKnownVersion; | ||
| } | ||
| } | ||
| 
     | 
||
| 
          
            
          
           | 
    @@ -287,8 +310,12 @@ public Executor executor() { | |
| public void handleResponse(HandshakeResponse response) { | ||
| if (isDone.compareAndSet(false, true)) { | ||
| ActionListener.completeWith(listener, () -> { | ||
| ensureCompatibleVersion(version, response.getTransportVersion(), response.getReleaseVersion(), channel); | ||
| final var resultVersion = TransportVersion.min(TransportHandshaker.this.version, response.getTransportVersion()); | ||
| final var resultVersion = ensureCompatibleVersion( | ||
| version, | ||
| response.getTransportVersion(), | ||
| response.getReleaseVersion(), | ||
| channel | ||
| ); | ||
| assert TransportVersion.current().before(version) // simulating a newer-version transport service for test purposes | ||
| || resultVersion.isKnown() : "negotiated unknown version " + resultVersion; | ||
| return resultVersion; | ||
| 
          
            
          
           | 
    ||
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.
At one point we had the map as a NavigableMap, which would allow quickly finding the closest known version. Is the linear performance here "ok" because handshakes are only done on initial connection?
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.
Yep we hardly ever open new connections.