Skip to content

Commit c3326fd

Browse files
committed
GODRIVER-1734 Fix txnNumber addition for retryable writes (#497)
1 parent dda448f commit c3326fd

File tree

2 files changed

+55
-5
lines changed

2 files changed

+55
-5
lines changed

mongo/client.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,8 @@ func (c *Client) StartSession(opts ...*options.SessionOptions) (Session, error)
275275
return nil, replaceErrors(err)
276276
}
277277

278-
sess.RetryWrite = c.retryWrites
278+
// Writes are not retryable on standalones, so let operation determine whether to retry
279+
sess.RetryWrite = false
279280
sess.RetryRead = c.retryReads
280281

281282
return &sessionImpl{

mongo/integration/retryable_writes_prose_test.go

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
package integration
88

99
import (
10+
"bytes"
1011
"testing"
1112

1213
"go.mongodb.org/mongo-driver/bson"
1314
"go.mongodb.org/mongo-driver/internal/testutil/assert"
15+
"go.mongodb.org/mongo-driver/mongo"
1416
"go.mongodb.org/mongo-driver/mongo/integration/mtest"
1517
"go.mongodb.org/mongo-driver/mongo/options"
1618
"go.mongodb.org/mongo-driver/x/mongo/driver"
@@ -19,12 +21,12 @@ import (
1921
func TestRetryableWritesProse(t *testing.T) {
2022
clientOpts := options.Client().SetRetryWrites(true).SetWriteConcern(mtest.MajorityWc).
2123
SetReadConcern(mtest.MajorityRc)
22-
mtOpts := mtest.NewOptions().ClientOptions(clientOpts).MinServerVersion("3.6").Topologies(mtest.ReplicaSet, mtest.Sharded).
23-
CreateClient(false)
24+
mtOpts := mtest.NewOptions().ClientOptions(clientOpts).MinServerVersion("3.6").CreateClient(false)
2425
mt := mtest.New(t, mtOpts)
2526
defer mt.Close()
2627

27-
mt.RunOpts("txn number included", noClientOpts, func(mt *mtest.T) {
28+
includeOpts := mtest.NewOptions().Topologies(mtest.ReplicaSet, mtest.Sharded).CreateClient(false)
29+
mt.RunOpts("txn number included", includeOpts, func(mt *mtest.T) {
2830
updateDoc := bson.D{{"$inc", bson.D{{"x", 1}}}}
2931
insertOneDoc := bson.D{{"x", 1}}
3032
insertManyOrderedArgs := bson.D{
@@ -75,7 +77,8 @@ func TestRetryableWritesProse(t *testing.T) {
7577
})
7678
}
7779
})
78-
mt.Run("wrap mmapv1 error", func(mt *mtest.T) {
80+
errorOpts := mtest.NewOptions().Topologies(mtest.ReplicaSet, mtest.Sharded)
81+
mt.RunOpts("wrap mmapv1 error", errorOpts, func(mt *mtest.T) {
7982
res, err := mt.DB.RunCommand(mtest.Background, bson.D{{"serverStatus", 1}}).DecodeBytes()
8083
assert.Nil(mt, err, "serverStatus error: %v", err)
8184
storageEngine, ok := res.Lookup("storageEngine", "name").StringValueOK()
@@ -87,4 +90,50 @@ func TestRetryableWritesProse(t *testing.T) {
8790
assert.Equal(mt, driver.ErrUnsupportedStorageEngine, err,
8891
"expected error %v, got %v", driver.ErrUnsupportedStorageEngine, err)
8992
})
93+
94+
standaloneOpts := mtest.NewOptions().Topologies(mtest.Single).CreateClient(false)
95+
mt.RunOpts("transaction number not sent on writes", standaloneOpts, func(mt *mtest.T) {
96+
mt.Run("explicit session", func(mt *mtest.T) {
97+
// Standalones do not support retryable writes and will error if a transaction number is sent
98+
99+
sess, err := mt.Client.StartSession()
100+
assert.Nil(mt, err, "StartSession error: %v", err)
101+
defer sess.EndSession(mtest.Background)
102+
103+
mt.ClearEvents()
104+
105+
err = mongo.WithSession(mtest.Background, sess, func(ctx mongo.SessionContext) error {
106+
doc := bson.D{{"foo", 1}}
107+
_, err := mt.Coll.InsertOne(ctx, doc)
108+
return err
109+
})
110+
assert.Nil(mt, err, "InsertOne error: %v", err)
111+
112+
_, wantID := sess.ID().Lookup("id").Binary()
113+
command := mt.GetStartedEvent().Command
114+
lsid, err := command.LookupErr("lsid")
115+
assert.Nil(mt, err, "Error getting lsid: %v", err)
116+
_, gotID := lsid.Document().Lookup("id").Binary()
117+
assert.True(mt, bytes.Equal(wantID, gotID), "expected session ID %v, got %v", wantID, gotID)
118+
txnNumber, err := command.LookupErr("txnNumber")
119+
assert.NotNil(mt, err, "expected no txnNumber, got %v", txnNumber)
120+
})
121+
mt.Run("implicit session", func(mt *mtest.T) {
122+
// Standalones do not support retryable writes and will error if a transaction number is sent
123+
124+
mt.ClearEvents()
125+
126+
doc := bson.D{{"foo", 1}}
127+
_, err := mt.Coll.InsertOne(mtest.Background, doc)
128+
assert.Nil(mt, err, "InsertOne error: %v", err)
129+
130+
command := mt.GetStartedEvent().Command
131+
lsid, err := command.LookupErr("lsid")
132+
assert.Nil(mt, err, "Error getting lsid: %v", err)
133+
_, gotID := lsid.Document().Lookup("id").Binary()
134+
assert.NotNil(mt, gotID, "expected session ID, got nil")
135+
txnNumber, err := command.LookupErr("txnNumber")
136+
assert.NotNil(mt, err, "expected no txnNumber, got %v", txnNumber)
137+
})
138+
})
90139
}

0 commit comments

Comments
 (0)