Skip to content

Commit a484eb6

Browse files
author
Divjot Arora
authored
GODRIVER-1582 Add support for commitQuorum (#388)
1 parent 68130e6 commit a484eb6

File tree

5 files changed

+197
-23
lines changed

5 files changed

+197
-23
lines changed

mongo/index_view.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,14 @@ func (iv IndexView) CreateMany(ctx context.Context, models []IndexModel, opts ..
238238
if option.MaxTime != nil {
239239
op.MaxTimeMS(int64(*option.MaxTime / time.Millisecond))
240240
}
241+
if option.CommitQuorum != nil {
242+
commitQuorum, err := transformValue(iv.coll.registry, option.CommitQuorum)
243+
if err != nil {
244+
return nil, err
245+
}
246+
247+
op.CommitQuorum(commitQuorum)
248+
}
241249

242250
err = op.Execute(ctx)
243251
if err != nil {

mongo/integration/index_view_test.go

Lines changed: 118 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,55 @@ func TestIndexView(t *testing.T) {
137137
})
138138
assert.NotNil(mt, err, "expected CreateOne error, got nil")
139139
})
140+
// Only run on replica sets as commitQuorum is not supported on standalones.
141+
mt.RunOpts("commit quorum", mtest.NewOptions().Topologies(mtest.ReplicaSet).CreateClient(false), func(mt *mtest.T) {
142+
intVal := options.CreateIndexes().SetCommitQuorumInt(1)
143+
stringVal := options.CreateIndexes().SetCommitQuorumString("majority")
144+
majority := options.CreateIndexes().SetCommitQuorumMajority()
145+
votingMembers := options.CreateIndexes().SetCommitQuorumVotingMembers()
146+
147+
indexModel := mongo.IndexModel{
148+
Keys: bson.D{{"x", 1}},
149+
}
150+
151+
testCases := []struct {
152+
name string
153+
opts *options.CreateIndexesOptions
154+
expectError bool
155+
expectedValue interface{} // ignored if expectError is true
156+
minServerVersion string
157+
maxServerVersion string
158+
}{
159+
{"error on server versions before 4.4", majority, true, nil, "", "4.2"},
160+
{"integer value", intVal, false, int32(1), "4.4", ""},
161+
{"string value", stringVal, false, "majority", "4.4", ""},
162+
{"majority", majority, false, "majority", "4.4", ""},
163+
{"votingMembers", votingMembers, false, "votingMembers", "4.4", ""},
164+
}
165+
for _, tc := range testCases {
166+
mtOpts := mtest.NewOptions().MinServerVersion(tc.minServerVersion).MaxServerVersion(tc.maxServerVersion)
167+
mt.RunOpts(tc.name, mtOpts, func(mt *mtest.T) {
168+
mt.ClearEvents()
169+
_, err := mt.Coll.Indexes().CreateOne(mtest.Background, indexModel, tc.opts)
170+
if tc.expectError {
171+
assert.NotNil(mt, err, "expected CreateOne error, got nil")
172+
return
173+
}
174+
175+
assert.Nil(mt, err, "CreateOne error: %v", err)
176+
cmd := mt.GetStartedEvent().Command
177+
sentBSONValue, err := cmd.LookupErr("commitQuorum")
178+
assert.Nil(mt, err, "expected commitQuorum in command %s", cmd)
179+
180+
var sentValue interface{}
181+
err = sentBSONValue.Unmarshal(&sentValue)
182+
assert.Nil(mt, err, "Unmarshal error: %v", err)
183+
184+
assert.Equal(mt, tc.expectedValue, sentValue, "expected commitQuorum value %v, got %v",
185+
tc.expectedValue, sentValue)
186+
})
187+
}
188+
})
140189
})
141190
mt.Run("create many", func(mt *mtest.T) {
142191
mt.Run("success", func(mt *mtest.T) {
@@ -162,31 +211,79 @@ func TestIndexView(t *testing.T) {
162211
})
163212
})
164213
wc := writeconcern.New(writeconcern.W(1))
165-
mt.RunOpts("uses writeconcern",
166-
mtest.NewOptions().CollectionOptions(options.Collection().SetWriteConcern(wc)),
167-
func(mt *mtest.T) {
168-
iv := mt.Coll.Indexes()
169-
_, err := iv.CreateMany(mtest.Background, []mongo.IndexModel{
170-
{
171-
Keys: bson.D{{"foo", -1}},
172-
},
173-
{
174-
Keys: bson.D{{"bar", 1}, {"baz", -1}},
175-
},
176-
})
177-
assert.Nil(mt, err, "CreateMany error: %v", err)
214+
wcMtOpts := mtest.NewOptions().CollectionOptions(options.Collection().SetWriteConcern(wc))
215+
mt.RunOpts("uses writeconcern", wcMtOpts, func(mt *mtest.T) {
216+
iv := mt.Coll.Indexes()
217+
_, err := iv.CreateMany(mtest.Background, []mongo.IndexModel{
218+
{
219+
Keys: bson.D{{"foo", -1}},
220+
},
221+
{
222+
Keys: bson.D{{"bar", 1}, {"baz", -1}},
223+
},
224+
})
225+
assert.Nil(mt, err, "CreateMany error: %v", err)
178226

179-
evt := mt.GetStartedEvent()
180-
assert.NotNil(mt, evt, "expected CommandStartedEvent, got nil")
227+
evt := mt.GetStartedEvent()
228+
assert.NotNil(mt, evt, "expected CommandStartedEvent, got nil")
181229

182-
assert.Equal(mt, "createIndexes", evt.CommandName, "command name mismatch; expected createIndexes, got %s", evt.CommandName)
230+
assert.Equal(mt, "createIndexes", evt.CommandName, "command name mismatch; expected createIndexes, got %s", evt.CommandName)
183231

184-
actual, err := evt.Command.LookupErr("writeConcern", "w")
185-
assert.Nil(mt, err, "error getting writeConcern.w: %s", err)
232+
actual, err := evt.Command.LookupErr("writeConcern", "w")
233+
assert.Nil(mt, err, "error getting writeConcern.w: %s", err)
186234

187-
wcVal := numberFromValue(mt, actual)
188-
assert.Equal(mt, int64(1), wcVal, "expected writeConcern to be 1, got: %v", wcVal)
189-
})
235+
wcVal := numberFromValue(mt, actual)
236+
assert.Equal(mt, int64(1), wcVal, "expected writeConcern to be 1, got: %v", wcVal)
237+
})
238+
// Only run on replica sets as commitQuorum is not supported on standalones.
239+
mt.RunOpts("commit quorum", mtest.NewOptions().Topologies(mtest.ReplicaSet).CreateClient(false), func(mt *mtest.T) {
240+
intVal := options.CreateIndexes().SetCommitQuorumInt(1)
241+
stringVal := options.CreateIndexes().SetCommitQuorumString("majority")
242+
majority := options.CreateIndexes().SetCommitQuorumMajority()
243+
votingMembers := options.CreateIndexes().SetCommitQuorumVotingMembers()
244+
245+
indexModel := mongo.IndexModel{
246+
Keys: bson.D{{"x", 1}},
247+
}
248+
249+
testCases := []struct {
250+
name string
251+
opts *options.CreateIndexesOptions
252+
expectError bool
253+
expectedValue interface{} // ignored if expectError is true
254+
minServerVersion string
255+
maxServerVersion string
256+
}{
257+
{"error on server versions before 4.4", majority, true, nil, "", "4.2"},
258+
{"integer value", intVal, false, int32(1), "4.4", ""},
259+
{"string value", stringVal, false, "majority", "4.4", ""},
260+
{"majority", majority, false, "majority", "4.4", ""},
261+
{"votingMembers", votingMembers, false, "votingMembers", "4.4", ""},
262+
}
263+
for _, tc := range testCases {
264+
mtOpts := mtest.NewOptions().MinServerVersion(tc.minServerVersion).MaxServerVersion(tc.maxServerVersion)
265+
mt.RunOpts(tc.name, mtOpts, func(mt *mtest.T) {
266+
mt.ClearEvents()
267+
_, err := mt.Coll.Indexes().CreateOne(mtest.Background, indexModel, tc.opts)
268+
if tc.expectError {
269+
assert.NotNil(mt, err, "expected CreateOne error, got nil")
270+
return
271+
}
272+
273+
assert.Nil(mt, err, "CreateOne error: %v", err)
274+
cmd := mt.GetStartedEvent().Command
275+
sentBSONValue, err := cmd.LookupErr("commitQuorum")
276+
assert.Nil(mt, err, "expected commitQuorum in command %s", cmd)
277+
278+
var sentValue interface{}
279+
err = sentBSONValue.Unmarshal(&sentValue)
280+
assert.Nil(mt, err, "Unmarshal error: %v", err)
281+
282+
assert.Equal(mt, tc.expectedValue, sentValue, "expected commitQuorum value %v, got %v",
283+
tc.expectedValue, sentValue)
284+
})
285+
}
286+
})
190287
})
191288
mt.Run("drop one", func(mt *mtest.T) {
192289
iv := mt.Coll.Indexes()

mongo/options/indexoptions.go

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@ import (
1313
// CreateIndexesOptions represents options that can be used to configure IndexView.CreateOne and IndexView.CreateMany
1414
// operations.
1515
type CreateIndexesOptions struct {
16+
// The number of data-bearing members of a replica set, including the primary, that must complete the index builds
17+
// successfully before the primary marks the indexes as ready. This should either be a string or int32 value. The
18+
// semantics of the values are as follows:
19+
//
20+
// 1. String: specifies a tag. All members with that tag must complete the build.
21+
// 2. int: the number of members that must complete the build.
22+
// 3. "majority": A special value to indicate that more than half the nodes must complete the build.
23+
// 4. "votingMembers": A special value to indicate that all voting data-bearing nodes must complete.
24+
//
25+
// This option is only available on MongoDB versions >= 4.4. A client-side error will be returned if the option
26+
// is specified for MongoDB versions <= 4.2. The default value is nil, meaning that the server-side default will be
27+
// used. See dochub.mongodb.org/core/index-commit-quorum for more information.
28+
CommitQuorum interface{}
29+
1630
// The maximum amount of time that the query can run on the server. The default value is nil, meaning that there
1731
// is no time limit for query execution.
1832
MaxTime *time.Duration
@@ -29,6 +43,30 @@ func (c *CreateIndexesOptions) SetMaxTime(d time.Duration) *CreateIndexesOptions
2943
return c
3044
}
3145

46+
// SetCommitQuorumInt sets the value for the CommitQuorum field as an int32.
47+
func (c *CreateIndexesOptions) SetCommitQuorumInt(quorum int32) *CreateIndexesOptions {
48+
c.CommitQuorum = quorum
49+
return c
50+
}
51+
52+
// SetCommitQuorumString sets the value for the CommitQuorum field as a string.
53+
func (c *CreateIndexesOptions) SetCommitQuorumString(quorum string) *CreateIndexesOptions {
54+
c.CommitQuorum = quorum
55+
return c
56+
}
57+
58+
// SetCommitQuorumMajority sets the value for the CommitQuorum to special "majority" value.
59+
func (c *CreateIndexesOptions) SetCommitQuorumMajority() *CreateIndexesOptions {
60+
c.CommitQuorum = "majority"
61+
return c
62+
}
63+
64+
// SetCommitQuorumVotingMembers sets the value for the CommitQuorum to special "votingMembers" value.
65+
func (c *CreateIndexesOptions) SetCommitQuorumVotingMembers() *CreateIndexesOptions {
66+
c.CommitQuorum = "votingMembers"
67+
return c
68+
}
69+
3270
// MergeCreateIndexesOptions combines the given CreateIndexesOptions into a single CreateIndexesOptions in a last one
3371
// wins fashion.
3472
func MergeCreateIndexesOptions(opts ...*CreateIndexesOptions) *CreateIndexesOptions {
@@ -40,6 +78,9 @@ func MergeCreateIndexesOptions(opts ...*CreateIndexesOptions) *CreateIndexesOpti
4078
if opt.MaxTime != nil {
4179
c.MaxTime = opt.MaxTime
4280
}
81+
if opt.CommitQuorum != nil {
82+
c.CommitQuorum = opt.CommitQuorum
83+
}
4384
}
4485

4586
return c
@@ -129,8 +170,8 @@ func MergeListIndexesOptions(opts ...*ListIndexesOptions) *ListIndexesOptions {
129170
// IndexOptions represents options that can be used to configure a new index created through the IndexView.CreateOne
130171
// or IndexView.CreateMany operations.
131172
type IndexOptions struct {
132-
// If true, the index will be built in the background on the server and will not block other tasks. The default
133-
// value is false.
173+
// If true, the index will be built in the background on the server and will not block other tasks. This option
174+
// has been deprecated in MongoDB version 4.2. The default value is false.
134175
Background *bool
135176

136177
// The length of time, in seconds, for documents to remain in the collection. The default value is 0, which means

x/mongo/driver/operation/createIndexes.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x/mongo/driver/operation/createIndexes.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ documentation = "An array containing index specification documents for the index
1818
type = "int64"
1919
documentation = "MaxTimeMS specifies the maximum amount of time to allow the query to run."
2020

21+
[request.commitQuorum]
22+
type = "value"
23+
minWireVersionRequired = 9
24+
documentation = """
25+
The number of data-bearing members of a replica set, including the primary, that must complete the index builds
26+
successfully before the primary marks the indexes as ready. This should either be a string or int32 value.
27+
"""
28+
2129
[response]
2230
name = "CreateIndexesResult"
2331

0 commit comments

Comments
 (0)