Skip to content

Commit a8fa12a

Browse files
GODRIVER-3054 Handshake connection should not use legacy for LB (#1482)
1 parent 919d4ae commit a8fa12a

File tree

6 files changed

+60
-28
lines changed

6 files changed

+60
-28
lines changed

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ evg-test-load-balancers:
153153
go test $(BUILD_TAGS) ./mongo/integration -run TestChangeStreamSpec -v -timeout $(TEST_TIMEOUT)s >> test.suite
154154
go test $(BUILD_TAGS) ./mongo/integration -run TestInitialDNSSeedlistDiscoverySpec/load_balanced -v -timeout $(TEST_TIMEOUT)s >> test.suite
155155
go test $(BUILD_TAGS) ./mongo/integration -run TestLoadBalancerSupport -v -timeout $(TEST_TIMEOUT)s >> test.suite
156+
go test $(BUILD_TAGS) ./mongo/integration -run TestLoadBalancedConnectionHandshake -v -timeout $(TEST_TIMEOUT)s >> test.suite
156157
go test $(BUILD_TAGS) ./mongo/integration/unified -run TestUnifiedSpec -v -timeout $(TEST_TIMEOUT)s >> test.suite
157158

158159
.PHONY: evg-test-search-index

mongo/integration/client_test.go

-21
Original file line numberDiff line numberDiff line change
@@ -768,27 +768,6 @@ func TestClient(t *testing.T) {
768768
"expected 'OP_MSG' OpCode in wire message, got %q", pair.Sent.OpCode.String())
769769
}
770770
})
771-
772-
// Test that OP_MSG is used for handshakes when loadBalanced is true.
773-
opMsgLBOpts := mtest.NewOptions().ClientType(mtest.Proxy).MinServerVersion("5.0").Topologies(mtest.LoadBalanced)
774-
mt.RunOpts("OP_MSG used for handshakes when loadBalanced is true", opMsgLBOpts, func(mt *mtest.T) {
775-
err := mt.Client.Ping(context.Background(), mtest.PrimaryRp)
776-
assert.Nil(mt, err, "Ping error: %v", err)
777-
778-
msgPairs := mt.GetProxiedMessages()
779-
assert.True(mt, len(msgPairs) >= 3, "expected at least 3 events, got %v", len(msgPairs))
780-
781-
// First three messages should be connection handshakes: one for the heartbeat connection, another for the
782-
// application connection, and a final one for the RTT monitor connection.
783-
for idx, pair := range msgPairs[:3] {
784-
assert.Equal(mt, "hello", pair.CommandName, "expected command name 'hello' at index %d, got %s", idx,
785-
pair.CommandName)
786-
787-
// Assert that appended OpCode is OP_MSG when loadBalanced is true.
788-
assert.Equal(mt, wiremessage.OpMsg, pair.Sent.OpCode,
789-
"expected 'OP_MSG' OpCode in wire message, got %q", pair.Sent.OpCode.String())
790-
}
791-
})
792771
}
793772

794773
func TestClient_BSONOptions(t *testing.T) {

mongo/integration/handshake_test.go

+51
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"go.mongodb.org/mongo-driver/mongo/integration/mtest"
2121
"go.mongodb.org/mongo-driver/version"
2222
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
23+
"go.mongodb.org/mongo-driver/x/mongo/driver/wiremessage"
2324
)
2425

2526
func TestHandshakeProse(t *testing.T) {
@@ -199,3 +200,53 @@ func TestHandshakeProse(t *testing.T) {
199200
})
200201
}
201202
}
203+
204+
func TestLoadBalancedConnectionHandshake(t *testing.T) {
205+
mt := mtest.New(t)
206+
207+
lbopts := mtest.NewOptions().ClientType(mtest.Proxy).Topologies(
208+
mtest.LoadBalanced)
209+
210+
mt.RunOpts("LB connection handshake uses OP_MSG", lbopts, func(mt *mtest.T) {
211+
// Ping the server to ensure the handshake has completed.
212+
err := mt.Client.Ping(context.Background(), nil)
213+
require.NoError(mt, err, "Ping error: %v", err)
214+
215+
messages := mt.GetProxiedMessages()
216+
handshakeMessage := messages[:1][0]
217+
218+
// Per the specifications, if loadBalanced=true, drivers MUST use the hello
219+
// command for the initial handshake and use the OP_MSG protocol.
220+
assert.Equal(mt, "hello", handshakeMessage.CommandName)
221+
assert.Equal(mt, wiremessage.OpMsg, handshakeMessage.Sent.OpCode)
222+
})
223+
224+
opts := mtest.NewOptions().ClientType(mtest.Proxy).Topologies(
225+
mtest.ReplicaSet,
226+
mtest.Sharded,
227+
mtest.Single,
228+
mtest.ShardedReplicaSet)
229+
230+
mt.RunOpts("non-LB connection handshake uses OP_QUERY", opts, func(mt *mtest.T) {
231+
// Ping the server to ensure the handshake has completed.
232+
err := mt.Client.Ping(context.Background(), nil)
233+
require.NoError(mt, err, "Ping error: %v", err)
234+
235+
messages := mt.GetProxiedMessages()
236+
handshakeMessage := messages[:1][0]
237+
238+
want := wiremessage.OpQuery
239+
240+
hello := handshake.LegacyHello
241+
if os.Getenv("REQUIRE_API_VERSION") == "true" {
242+
hello = "hello"
243+
244+
// If the server API version is requested, then we should use OP_MSG
245+
// regardless of the topology
246+
want = wiremessage.OpMsg
247+
}
248+
249+
assert.Equal(mt, hello, handshakeMessage.CommandName)
250+
assert.Equal(mt, want, handshakeMessage.Sent.OpCode)
251+
})
252+
}

testdata/load-balancers/sdam-error-handling.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,8 @@
279279
},
280280
"data": {
281281
"failCommands": [
282-
"isMaster"
282+
"isMaster",
283+
"hello"
283284
],
284285
"closeConnection": true,
285286
"appName": "lbSDAMErrorTestClient"

testdata/load-balancers/sdam-error-handling.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ tests:
153153
configureFailPoint: failCommand
154154
mode: { times: 1 }
155155
data:
156-
failCommands: [isMaster]
156+
failCommands: [isMaster, hello]
157157
closeConnection: true
158158
appName: *singleClientAppName
159159
- name: insertOne

x/mongo/driver/operation/hello.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ func (h *Hello) handshakeCommand(dst []byte, desc description.SelectedServer) ([
530530
func (h *Hello) command(dst []byte, desc description.SelectedServer) ([]byte, error) {
531531
// Use "hello" if topology is LoadBalanced, API version is declared or server
532532
// has responded with "helloOk". Otherwise, use legacy hello.
533-
if desc.Kind == description.LoadBalanced || h.serverAPI != nil || desc.Server.HelloOK {
533+
if h.loadBalanced || h.serverAPI != nil || desc.Server.HelloOK {
534534
dst = bsoncore.AppendInt32Element(dst, "hello", 1)
535535
} else {
536536
dst = bsoncore.AppendInt32Element(dst, handshake.LegacyHello, 1)
@@ -575,8 +575,8 @@ func (h *Hello) StreamResponse(ctx context.Context, conn driver.StreamerConnecti
575575
// loadBalanced is False. If this is the case, then the drivers MUST use legacy
576576
// hello for the first message of the initial handshake with the OP_QUERY
577577
// protocol
578-
func isLegacyHandshake(srvAPI *driver.ServerAPIOptions, deployment driver.Deployment) bool {
579-
return srvAPI == nil && deployment.Kind() != description.LoadBalanced
578+
func isLegacyHandshake(srvAPI *driver.ServerAPIOptions, loadbalanced bool) bool {
579+
return srvAPI == nil && !loadbalanced
580580
}
581581

582582
func (h *Hello) createOperation() driver.Operation {
@@ -592,7 +592,7 @@ func (h *Hello) createOperation() driver.Operation {
592592
ServerAPI: h.serverAPI,
593593
}
594594

595-
if isLegacyHandshake(h.serverAPI, h.d) {
595+
if isLegacyHandshake(h.serverAPI, h.loadBalanced) {
596596
op.Legacy = driver.LegacyHandshake
597597
}
598598

@@ -616,7 +616,7 @@ func (h *Hello) GetHandshakeInformation(ctx context.Context, _ address.Address,
616616
ServerAPI: h.serverAPI,
617617
}
618618

619-
if isLegacyHandshake(h.serverAPI, deployment) {
619+
if isLegacyHandshake(h.serverAPI, h.loadBalanced) {
620620
op.Legacy = driver.LegacyHandshake
621621
}
622622

0 commit comments

Comments
 (0)