From 29672666e5a4edc068dde744deb6ae546b34edbf Mon Sep 17 00:00:00 2001 From: Alf-Rune Siqveland Date: Mon, 11 Jul 2022 15:21:08 +0200 Subject: [PATCH 1/6] fix: Correct broken document migration in 2.0.0 Changelog: Title Ticket: MEN-5718 Signed-off-by: Alf-Rune Siqveland (cherry picked from commit fce72debe1739c4c3bfbd58f8f592fbbf3d19a0b) --- store/mongo/migration_2_0_0.go | 26 +++---- store/mongo/migration_2_0_0_test.go | 101 +++++++++++++++++++++------- 2 files changed, 86 insertions(+), 41 deletions(-) diff --git a/store/mongo/migration_2_0_0.go b/store/mongo/migration_2_0_0.go index a5662d2c..cadec72c 100644 --- a/store/mongo/migration_2_0_0.go +++ b/store/mongo/migration_2_0_0.go @@ -1,4 +1,4 @@ -// Copyright 2021 Northern.tech AS +// Copyright 2022 Northern.tech AS // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -43,18 +43,7 @@ func (m *migration_2_0_0) Up(from migrate.Version) error { collections := map[string]struct { Indexes []mongo.IndexModel }{ - DevicesCollectionName: { - Indexes: []mongo.IndexModel{ - { - Keys: bson.D{ - {Key: mstore.FieldTenantID, Value: 1}, - {Key: dbFieldID, Value: 1}, - }, - Options: mopts.Index(). - SetName(mstore.FieldTenantID + "_" + dbFieldID), - }, - }, - }, + DevicesCollectionName: {}, SessionsCollectionName: { Indexes: []mongo.IndexModel{ { @@ -105,16 +94,18 @@ func (m *migration_2_0_0) Up(from migrate.Version) error { Tenant: tenantID, }) - for collection := range collections { + for collection, idxes := range collections { // get all the documents in the collection findOptions := mopts.Find(). SetBatchSize(findBatchSize). SetSort(bson.D{{Key: dbFieldID, Value: 1}}) coll := client.Database(m.db).Collection(collection) collOut := client.Database(DbName).Collection(collection) - _, err := collOut.Indexes().CreateMany(ctx, collections[collection].Indexes) - if err != nil { - return err + if len(idxes.Indexes) > 0 { + _, err := collOut.Indexes().CreateMany(ctx, collections[collection].Indexes) + if err != nil { + return err + } } cur, err := coll.Find(ctx, bson.D{}, findOptions) @@ -148,6 +139,7 @@ func (m *migration_2_0_0) Up(from migrate.Version) error { writes = append(writes, mongo.NewInsertOneModel().SetDocument(item)) } if len(writes) == findBatchSize { + _, err = collOut.BulkWrite(ctx, writes) if err != nil { return err } diff --git a/store/mongo/migration_2_0_0_test.go b/store/mongo/migration_2_0_0_test.go index ccb0a6b4..6c0fad60 100644 --- a/store/mongo/migration_2_0_0_test.go +++ b/store/mongo/migration_2_0_0_test.go @@ -1,4 +1,4 @@ -// Copyright 2021 Northern.tech AS +// Copyright 2022 Northern.tech AS // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,10 +16,12 @@ package mongo import ( "context" + "strconv" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.mongodb.org/mongo-driver/bson" "github.com/mendersoftware/go-lib-micro/mongo/migrate" mstore "github.com/mendersoftware/go-lib-micro/store/v2" @@ -37,36 +39,87 @@ func TestMigration_2_0_0(t *testing.T) { db: DbName, } from := migrate.MakeVersion(0, 0, 0) + db := db.Client().Database(DbName) - err := m.Up(from) - require.NoError(t, err) - - iv := db.Client().Database(DbName). - Collection(DevicesCollectionName). - Indexes() + collDevs := db.Client().Database(DbName). + Collection(DevicesCollectionName) ctx := context.Background() - cur, err := iv.List(ctx) - require.NoError(t, err) - var idxes []index - err = cur.All(ctx, &idxes) + _, err := collDevs.InsertMany(ctx, func() []interface{} { + ret := make([]interface{}, findBatchSize+1) + for i := range ret { + ret[i] = bson.D{{Key: "_id", Value: strconv.Itoa(i)}} + } + return ret + }()) + if !assert.NoError(t, err) { + t.FailNow() + } + err = m.Up(from) require.NoError(t, err) - require.Len(t, idxes, 2) - for _, idx := range idxes { - if _, ok := idx.Keys[dbFieldID]; ok && len(idx.Keys) == 1 { - // Skip default index - continue + + collNames, err := db.ListCollectionNames(ctx, bson.D{}) + for _, collName := range collNames { + idxes, err := db.Collection(collName). + Indexes(). + ListSpecifications(ctx) + require.NoError(t, err) + expectedLen := 1 + switch collName { + case RecordingsCollectionName: + expectedLen = 3 + case SessionsCollectionName, ControlCollectionName: + expectedLen = 2 } - switch idx.Name { - case mstore.FieldTenantID + "_" + dbFieldID: - assert.Equal(t, map[string]int{ - dbFieldID: 1, - mstore.FieldTenantID: 1, - }, idx.Keys) + require.Len(t, idxes, expectedLen) + for _, idx := range idxes { + if idx.Name == "_id_" { + // Skip default index + continue + } + switch idx.Name { + case IndexNameLogsExpire: + var keys bson.D + assert.True(t, idx.ExpireAfterSeconds != nil, "Expected index to have TTL") + err := bson.Unmarshal(idx.KeysDocument, &keys) + require.NoError(t, err) + assert.Equal(t, bson.D{ + {Key: "expire_ts", Value: int32(1)}, + }, keys) - default: - assert.Failf(t, "index test failed", "Index name \"%s\" not recognized", idx.Name) + case mstore.FieldTenantID + "_" + dbFieldSessionID: + var keys bson.D + err := bson.Unmarshal(idx.KeysDocument, &keys) + require.NoError(t, err) + assert.Equal(t, bson.D{ + {Key: mstore.FieldTenantID, Value: int32(1)}, + {Key: dbFieldSessionID, Value: int32(1)}, + }, keys) + + case mstore.FieldTenantID + "_" + dbFieldDeviceID: + var keys bson.D + err := bson.Unmarshal(idx.KeysDocument, &keys) + require.NoError(t, err) + assert.Equal(t, bson.D{ + {Key: mstore.FieldTenantID, Value: int32(1)}, + {Key: dbFieldDeviceID, Value: int32(1)}, + }, keys) + + default: + assert.Failf(t, "index test failed", "Index name \"%s\" not recognized", idx.Name) + } } } assert.Equal(t, "2.0.0", m.Version().String()) + + actual, err := collDevs.CountDocuments(ctx, bson.D{ + {Key: mstore.FieldTenantID, Value: bson.D{{Key: "$exists", Value: true}}}, + }) + if assert.NoError(t, err) { + expected, err := collDevs.CountDocuments(ctx, bson.D{}) + if assert.NoError(t, err) { + assert.Equalf(t, expected, actual, + "%d documents does not contain 'tenant_id' key", expected-actual) + } + } } From 24b48e4a8190e7e85cb4f3115743ac73a07270d8 Mon Sep 17 00:00:00 2001 From: Alf-Rune Siqveland Date: Mon, 11 Jul 2022 16:26:35 +0200 Subject: [PATCH 2/6] fix: Apply migration to all databases prior to 2.0.0 When the service migrated to a single database model, the old databases was not included in the migration itself resulting in potential data loss. Changelog: Title Ticket: MEN-5718 Signed-off-by: Alf-Rune Siqveland (cherry picked from commit b07bcc9979835e44ea00e2d73ec33ca14dd7ccad) --- store/mongo/migration_2_0_0.go | 31 +++++++++---------- store/mongo/migration_2_0_0_test.go | 33 +++++++++++--------- store/mongo/migrations.go | 47 ++++++++++++++++++----------- 3 files changed, 62 insertions(+), 49 deletions(-) diff --git a/store/mongo/migration_2_0_0.go b/store/mongo/migration_2_0_0.go index cadec72c..0cc6c152 100644 --- a/store/mongo/migration_2_0_0.go +++ b/store/mongo/migration_2_0_0.go @@ -93,9 +93,10 @@ func (m *migration_2_0_0) Up(from migrate.Version) error { ctx = identity.WithContext(ctx, &identity.Identity{ Tenant: tenantID, }) + writes := make([]mongo.WriteModel, 0, findBatchSize) for collection, idxes := range collections { - // get all the documents in the collection + writes = writes[:0] findOptions := mopts.Find(). SetBatchSize(findBatchSize). SetSort(bson.D{{Key: dbFieldID, Value: 1}}) @@ -107,15 +108,25 @@ func (m *migration_2_0_0) Up(from migrate.Version) error { return err } } + if m.db == DbName { + coll.UpdateMany(ctx, bson.D{ + {Key: mstore.FieldTenantID, Value: bson.D{ + {Key: "$exists", Value: false}, + }}, + }, bson.D{{Key: "$set", Value: bson.D{ + {Key: mstore.FieldTenantID, Value: ""}, + }}}, + ) + continue + } + // get all the documents in the collection cur, err := coll.Find(ctx, bson.D{}, findOptions) if err != nil { return err } defer cur.Close(ctx) - writes := make([]mongo.WriteModel, 0, findBatchSize) - // migrate the documents for cur.Next(ctx) { item := bson.D{} @@ -125,19 +136,7 @@ func (m *migration_2_0_0) Up(from migrate.Version) error { } item = mstore.WithTenantID(ctx, item) - if m.db == DbName { - filter := bson.D{} - for _, i := range item { - if i.Key == dbFieldID { - filter = append(filter, i) - } - } - writes = append(writes, - mongo.NewReplaceOneModel(). - SetFilter(filter).SetReplacement(item)) - } else { - writes = append(writes, mongo.NewInsertOneModel().SetDocument(item)) - } + writes = append(writes, mongo.NewInsertOneModel().SetDocument(item)) if len(writes) == findBatchSize { _, err = collOut.BulkWrite(ctx, writes) if err != nil { diff --git a/store/mongo/migration_2_0_0_test.go b/store/mongo/migration_2_0_0_test.go index 6c0fad60..efd3ac75 100644 --- a/store/mongo/migration_2_0_0_test.go +++ b/store/mongo/migration_2_0_0_test.go @@ -16,14 +16,12 @@ package mongo import ( "context" - "strconv" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson" - "github.com/mendersoftware/go-lib-micro/mongo/migrate" mstore "github.com/mendersoftware/go-lib-micro/store/v2" ) @@ -38,24 +36,34 @@ func TestMigration_2_0_0(t *testing.T) { client: db.Client(), db: DbName, } - from := migrate.MakeVersion(0, 0, 0) db := db.Client().Database(DbName) - collDevs := db.Client().Database(DbName). - Collection(DevicesCollectionName) + collDevs := db.Collection(DevicesCollectionName) ctx := context.Background() _, err := collDevs.InsertMany(ctx, func() []interface{} { ret := make([]interface{}, findBatchSize+1) for i := range ret { - ret[i] = bson.D{{Key: "_id", Value: strconv.Itoa(i)}} + ret[i] = bson.D{} } return ret }()) if !assert.NoError(t, err) { t.FailNow() } - err = m.Up(from) + _, err = db.Client(). + Database(DbName+"-123456789012345678901234"). + Collection(DevicesCollectionName). + InsertMany(ctx, func() []interface{} { + ret := make([]interface{}, findBatchSize+1) + for i := range ret { + ret[i] = bson.D{} + } + return ret + }()) + require.NoError(t, err) + + err = Migrate(ctx, DbName, "2.0.0", db.Client(), true) require.NoError(t, err) collNames, err := db.ListCollectionNames(ctx, bson.D{}) @@ -113,13 +121,8 @@ func TestMigration_2_0_0(t *testing.T) { assert.Equal(t, "2.0.0", m.Version().String()) actual, err := collDevs.CountDocuments(ctx, bson.D{ - {Key: mstore.FieldTenantID, Value: bson.D{{Key: "$exists", Value: true}}}, + {Key: mstore.FieldTenantID, Value: bson.D{{Key: "$exists", Value: false}}}, }) - if assert.NoError(t, err) { - expected, err := collDevs.CountDocuments(ctx, bson.D{}) - if assert.NoError(t, err) { - assert.Equalf(t, expected, actual, - "%d documents does not contain 'tenant_id' key", expected-actual) - } - } + require.NoError(t, err) + assert.Zerof(t, actual, "%d documents are not indexed by tenant_id", actual) } diff --git a/store/mongo/migrations.go b/store/mongo/migrations.go index ee7ed23d..713343cf 100644 --- a/store/mongo/migrations.go +++ b/store/mongo/migrations.go @@ -1,4 +1,4 @@ -// Copyright 2021 Northern.tech AS +// Copyright 2022 Northern.tech AS // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import ( "github.com/mendersoftware/go-lib-micro/log" "github.com/mendersoftware/go-lib-micro/mongo/migrate" "github.com/pkg/errors" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" ) @@ -46,26 +47,36 @@ func Migrate(ctx context.Context, return errors.Wrap(err, "failed to parse service version") } - m := migrate.SimpleMigrator{ - Client: client, - Db: db, - Automigrate: automigrate, + dbNames, err := client.ListDatabaseNames(ctx, bson.D{ + {Key: "name", Value: bson.D{ + {Key: "$regex", Value: "^" + DbName + "-"}, + }}, + }) + if err != nil { + return err } - migrations := []migrate.Migration{ - &migration_1_0_0{ - client: client, - db: db, - }, - &migration_2_0_0{ - client: client, - db: db, - }, - } + for _, dbName := range append([]string{DbName}, dbNames...) { + m := migrate.SimpleMigrator{ + Client: client, + Db: dbName, + Automigrate: automigrate, + } - err = m.Apply(ctx, *ver, migrations) - if err != nil { - return errors.Wrap(err, "failed to apply migrations") + migrations := []migrate.Migration{ + &migration_1_0_0{ + client: client, + db: dbName, + }, + &migration_2_0_0{ + client: client, + db: dbName, + }, + } + err = m.Apply(ctx, *ver, migrations) + if err != nil { + return errors.Wrap(err, "failed to apply migrations") + } } return nil From 8e6e3429f4340ffda6b2a42723c2b1b5753889f6 Mon Sep 17 00:00:00 2001 From: Alf-Rune Siqveland Date: Mon, 11 Jul 2022 16:47:11 +0200 Subject: [PATCH 3/6] fix: New migration (2.0.1) patching 2.0.0 document migration Changelog: Title Ticket: MEN-5718 Signed-off-by: Alf-Rune Siqveland (cherry picked from commit 77f006871c54e68d0a74b5ca5ce6a62beb0dd839) --- ...{migration_2_0_0.go => migration_2_0_1.go} | 41 +++++++++++-------- ..._2_0_0_test.go => migration_2_0_1_test.go} | 11 ++--- store/mongo/migrations.go | 5 ++- 3 files changed, 30 insertions(+), 27 deletions(-) rename store/mongo/{migration_2_0_0.go => migration_2_0_1.go} (84%) rename store/mongo/{migration_2_0_0_test.go => migration_2_0_1_test.go} (94%) diff --git a/store/mongo/migration_2_0_0.go b/store/mongo/migration_2_0_1.go similarity index 84% rename from store/mongo/migration_2_0_0.go rename to store/mongo/migration_2_0_1.go index 0cc6c152..18b37dc9 100644 --- a/store/mongo/migration_2_0_0.go +++ b/store/mongo/migration_2_0_1.go @@ -31,12 +31,12 @@ const ( findBatchSize = 255 ) -type migration_2_0_0 struct { +type migration_2_0_1 struct { client *mongo.Client db string } -func (m *migration_2_0_0) Up(from migrate.Version) error { +func (m *migration_2_0_1) Up(from migrate.Version) error { ctx := context.Background() client := m.client @@ -100,16 +100,15 @@ func (m *migration_2_0_0) Up(from migrate.Version) error { findOptions := mopts.Find(). SetBatchSize(findBatchSize). SetSort(bson.D{{Key: dbFieldID, Value: 1}}) - coll := client.Database(m.db).Collection(collection) collOut := client.Database(DbName).Collection(collection) - if len(idxes.Indexes) > 0 { - _, err := collOut.Indexes().CreateMany(ctx, collections[collection].Indexes) - if err != nil { - return err - } - } if m.db == DbName { - coll.UpdateMany(ctx, bson.D{ + if len(idxes.Indexes) > 0 { + _, err := collOut.Indexes().CreateMany(ctx, collections[collection].Indexes) + if err != nil { + return err + } + } + _, err := collOut.UpdateMany(ctx, bson.D{ {Key: mstore.FieldTenantID, Value: bson.D{ {Key: "$exists", Value: false}, }}, @@ -117,9 +116,13 @@ func (m *migration_2_0_0) Up(from migrate.Version) error { {Key: mstore.FieldTenantID, Value: ""}, }}}, ) + if err != nil { + return err + } continue } + coll := client.Database(m.db).Collection(collection) // get all the documents in the collection cur, err := coll.Find(ctx, bson.D{}, findOptions) if err != nil { @@ -129,14 +132,16 @@ func (m *migration_2_0_0) Up(from migrate.Version) error { // migrate the documents for cur.Next(ctx) { - item := bson.D{} - err := cur.Decode(&item) - if err != nil { + id := cur.Current.Lookup(dbFieldID) + var item bson.D + if err = cur.Decode(&item); err != nil { return err } - - item = mstore.WithTenantID(ctx, item) - writes = append(writes, mongo.NewInsertOneModel().SetDocument(item)) + writes = append(writes, mongo. + NewReplaceOneModel(). + SetFilter(bson.D{{Key: dbFieldID, Value: id}}). + SetUpsert(true). + SetReplacement(mstore.WithTenantID(ctx, item))) if len(writes) == findBatchSize { _, err = collOut.BulkWrite(ctx, writes) if err != nil { @@ -156,6 +161,6 @@ func (m *migration_2_0_0) Up(from migrate.Version) error { return nil } -func (m *migration_2_0_0) Version() migrate.Version { - return migrate.MakeVersion(2, 0, 0) +func (m *migration_2_0_1) Version() migrate.Version { + return migrate.MakeVersion(2, 0, 1) } diff --git a/store/mongo/migration_2_0_0_test.go b/store/mongo/migration_2_0_1_test.go similarity index 94% rename from store/mongo/migration_2_0_0_test.go rename to store/mongo/migration_2_0_1_test.go index efd3ac75..ed8d3a08 100644 --- a/store/mongo/migration_2_0_0_test.go +++ b/store/mongo/migration_2_0_1_test.go @@ -31,11 +31,7 @@ type index struct { ExpireAfter *int `bson:"expireAfterSeconds"` } -func TestMigration_2_0_0(t *testing.T) { - m := &migration_2_0_0{ - client: db.Client(), - db: DbName, - } +func TestMigration_2_0_1(t *testing.T) { db := db.Client().Database(DbName) collDevs := db.Collection(DevicesCollectionName) @@ -63,7 +59,9 @@ func TestMigration_2_0_0(t *testing.T) { }()) require.NoError(t, err) - err = Migrate(ctx, DbName, "2.0.0", db.Client(), true) + // NOTE: We're using 2.0.1 since it's the same migration, thus verifying + // that the migration is reentrant. + err = Migrate(ctx, DbName, "2.0.1", db.Client(), true) require.NoError(t, err) collNames, err := db.ListCollectionNames(ctx, bson.D{}) @@ -118,7 +116,6 @@ func TestMigration_2_0_0(t *testing.T) { } } } - assert.Equal(t, "2.0.0", m.Version().String()) actual, err := collDevs.CountDocuments(ctx, bson.D{ {Key: mstore.FieldTenantID, Value: bson.D{{Key: "$exists", Value: false}}}, diff --git a/store/mongo/migrations.go b/store/mongo/migrations.go index 713343cf..dde19ba2 100644 --- a/store/mongo/migrations.go +++ b/store/mongo/migrations.go @@ -26,7 +26,7 @@ import ( const ( // DbVersion is the current schema version - DbVersion = "2.0.0" + DbVersion = "2.0.1" // DbName is the database name DbName = "deviceconnect" @@ -68,10 +68,11 @@ func Migrate(ctx context.Context, client: client, db: dbName, }, - &migration_2_0_0{ + &migration_2_0_1{ client: client, db: dbName, }, + // NOTE: Future migrations need only be applied to DbName } err = m.Apply(ctx, *ver, migrations) if err != nil { From 1604de195558ea666d63eaff8f1dba72970165db Mon Sep 17 00:00:00 2001 From: Alf-Rune Siqveland Date: Fri, 15 Jul 2022 17:20:17 +0200 Subject: [PATCH 4/6] ci: Inherit unit test image from imported project Changelog: None Signed-off-by: Alf-Rune Siqveland --- .gitlab-ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7f5f686d..b5e81670 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,8 +29,6 @@ include: file: '.gitlab-ci-check-docker-build.yml' test:unit: - image: golang:1.15 - needs: [] services: - name: mongo:4.4 alias: mender-mongo From ad097af982872dd6139a73630f1c32be4d7d1bdf Mon Sep 17 00:00:00 2001 From: Alf-Rune Siqveland Date: Fri, 15 Jul 2022 17:21:29 +0200 Subject: [PATCH 5/6] chore: Add license header to shell scripts Changelog: None Signed-off-by: Alf-Rune Siqveland --- tests/run.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/run.sh b/tests/run.sh index 4e96bed9..e482c810 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -1,4 +1,17 @@ #!/bin/sh +# Copyright 2022 Northern.tech AS +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # tests are supposed to be located in the same directory as this file From 73cf45e797087b776ce94631127817319bca35ad Mon Sep 17 00:00:00 2001 From: Alf-Rune Siqveland Date: Fri, 15 Jul 2022 17:29:33 +0200 Subject: [PATCH 6/6] chore: Upgrade mongo-driver to v1.9.1 Required by cherry-pick unit tests. Changelog: None Signed-off-by: Alf-Rune Siqveland --- go.mod | 2 +- go.sum | 5 +- .../go.mongodb.org/mongo-driver/bson/bson.go | 2 - .../mongo-driver/bson/bson_1_8.go | 81 - .../mongo-driver/bson/bsoncodec/bsoncodec.go | 2 +- .../bson/bsoncodec/default_value_decoders.go | 14 +- .../bson/bsoncodec/default_value_encoders.go | 11 +- .../mongo-driver/bson/bsoncodec/map_codec.go | 19 +- .../mongo-driver/bson/bsoncodec/registry.go | 14 +- .../bson/bsoncodec/struct_codec.go | 11 +- .../mongo-driver/bson/bsoncodec/types.go | 25 - .../bson/bsonrw/extjson_parser.go | 2 +- .../bson/bsonrw/extjson_wrappers.go | 6 +- .../bson/bsonrw/extjson_writer.go | 9 +- .../mongo-driver/bson/bsonrw/value_reader.go | 20 +- .../mongo-driver/bson/bsonrw/value_writer.go | 2 +- .../mongo-driver/bson/bsonrw/writer.go | 24 - .../mongo-driver/bson/bsontype/bsontype.go | 2 + .../go.mongodb.org/mongo-driver/bson/doc.go | 2 +- .../mongo-driver/bson/marshal.go | 25 + .../mongo-driver/bson/primitive/decimal.go | 11 +- .../mongo-driver/bson/primitive/objectid.go | 26 +- .../mongo-driver/bson/primitive/primitive.go | 2 +- .../mongo-driver/bson/primitive_codecs.go | 25 +- .../go.mongodb.org/mongo-driver/bson/raw.go | 7 - .../mongo-driver/bson/registry.go | 2 +- .../go.mongodb.org/mongo-driver/bson/types.go | 49 - .../mongo-driver/bson/unmarshal.go | 2 +- .../mongo-driver/event/monitoring.go | 15 +- .../mongo-driver/internal/const.go | 12 +- .../internal/randutil/randutil.go | 77 + .../mongo-driver/internal/semaphore.go | 57 - .../mongo-driver/internal/string_util.go | 2 +- .../internal/uri_validation_errors.go | 4 + .../mongo-driver/mongo/bulk_write.go | 28 +- .../mongo-driver/mongo/change_stream.go | 85 +- .../mongo/change_stream_deployment.go | 5 + .../mongo-driver/mongo/client.go | 47 +- .../mongo-driver/mongo/client_encryption.go | 3 +- .../mongo-driver/mongo/collection.go | 138 +- .../mongo-driver/mongo/cursor.go | 42 + .../mongo-driver/mongo/database.go | 12 + .../mongo-driver/mongo/description/server.go | 26 +- .../mongo/description/server_selector.go | 26 +- .../go.mongodb.org/mongo-driver/mongo/doc.go | 4 +- .../mongo-driver/mongo/errors.go | 38 + .../mongo-driver/mongo/index_view.go | 3 +- .../mongo-driver/mongo/mongo.go | 44 +- .../mongo-driver/mongo/mongocryptd.go | 2 + .../mongo/options/aggregateoptions.go | 23 +- .../mongo/options/autoencryptionoptions.go | 25 + .../mongo/options/bulkwriteoptions.go | 18 + .../mongo/options/changestreamoptions.go | 34 + .../mongo/options/clientencryptionoptions.go | 89 ++ .../mongo/options/clientoptions.go | 95 +- .../mongo/options/clientoptions_1_10.go | 13 - .../mongo/options/clientoptions_1_9.go | 20 - .../mongo/options/createcollectionoptions.go | 14 +- .../mongo/options/deleteoptions.go | 15 + .../mongo-driver/mongo/options/findoptions.go | 60 + .../mongo/options/indexoptions.go | 3 + .../mongo/options/listcollectionsoptions.go | 14 + .../mongo/options/listdatabasesoptions.go | 2 +- .../mongo/options/mongooptions.go | 4 +- .../mongo/options/replaceoptions.go | 15 + .../mongo/options/updateoptions.go | 15 + .../mongo-driver/mongo/readpref/options.go | 2 +- .../mongo-driver/mongo/readpref/readpref.go | 3 + .../mongo-driver/mongo/results.go | 26 +- .../mongo-driver/mongo/session.go | 7 +- .../mongo-driver/mongo/single_result.go | 26 + .../mongo/writeconcern/writeconcern.go | 2 +- .../mongo-driver/version/version.go | 2 +- .../x/bsonx/bsoncore/bson_documentbuilder.go | 2 +- .../mongo-driver/x/bsonx/bsoncore/bsoncore.go | 6 +- .../mongo-driver/x/bsonx/bsoncore/value.go | 4 +- .../mongo-driver/x/bsonx/document.go | 2 +- .../mongo-driver/x/bsonx/element.go | 2 - .../mongo-driver/x/bsonx/mdocument.go | 4 +- .../mongo-driver/x/bsonx/primitive_codecs.go | 1 - .../x/bsonx/reflectionfree_d_codec.go | 107 +- .../mongo-driver/x/bsonx/registry.go | 2 +- .../mongo-driver/x/bsonx/value.go | 52 +- .../mongo-driver/x/mongo/driver/DESIGN.md | 4 +- .../mongo-driver/x/mongo/driver/auth/auth.go | 7 +- .../x/mongo/driver/auth/aws_conv.go | 6 +- .../x/mongo/driver/auth/conversation.go | 2 +- .../x/mongo/driver/auth/default.go | 2 +- .../x/mongo/driver/auth/gssapi.go | 5 +- .../x/mongo/driver/auth/gssapi_not_enabled.go | 3 +- .../mongo/driver/auth/gssapi_not_supported.go | 3 +- .../mongo/driver/auth/internal/awsv4/rules.go | 20 +- .../driver/auth/internal/awsv4/signer.go | 4 +- .../mongo/driver/auth/internal/gssapi/gss.go | 5 +- .../mongo/driver/auth/internal/gssapi/sspi.go | 3 +- .../x/mongo/driver/auth/mongodbaws.go | 2 +- .../x/mongo/driver/auth/mongodbcr.go | 19 +- .../mongo-driver/x/mongo/driver/auth/sasl.go | 2 +- .../mongo-driver/x/mongo/driver/auth/util.go | 9 +- .../x/mongo/driver/batch_cursor.go | 28 +- .../mongo-driver/x/mongo/driver/batches.go | 4 - .../x/mongo/driver/connstring/connstring.go | 120 +- .../mongo-driver/x/mongo/driver/crypt.go | 78 +- .../mongo-driver/x/mongo/driver/dns/dns.go | 16 +- .../mongo-driver/x/mongo/driver/driver.go | 22 +- .../mongo-driver/x/mongo/driver/errors.go | 29 +- .../x/mongo/driver/mongocrypt/binary.go | 1 + .../x/mongo/driver/mongocrypt/errors.go | 1 + .../driver/mongocrypt/errors_not_enabled.go | 1 + .../x/mongo/driver/mongocrypt/mongocrypt.go | 1 + .../driver/mongocrypt/mongocrypt_context.go | 1 + .../mongocrypt_context_not_enabled.go | 1 + .../mongocrypt/mongocrypt_kms_context.go | 7 + .../mongocrypt_kms_context_not_enabled.go | 11 +- .../mongocrypt/mongocrypt_not_enabled.go | 1 + .../mongo-driver/x/mongo/driver/ocsp/ocsp.go | 100 +- .../mongo-driver/x/mongo/driver/operation.go | 383 +++-- .../driver/operation/abort_transaction.go | 8 +- .../driver/operation/abort_transaction.toml | 17 - .../x/mongo/driver/operation/aggregate.go | 39 +- .../x/mongo/driver/operation/aggregate.toml | 48 - .../x/mongo/driver/operation/command.go | 14 +- .../driver/operation/commit_transaction.go | 8 +- .../driver/operation/commit_transaction.toml | 22 - .../x/mongo/driver/operation/count.go | 28 +- .../x/mongo/driver/operation/count.toml | 26 - .../x/mongo/driver/operation/create.go | 36 +- .../x/mongo/driver/operation/create.toml | 62 - .../x/mongo/driver/operation/createIndexes.go | 27 +- .../mongo/driver/operation/createIndexes.toml | 42 - .../x/mongo/driver/operation/delete.go | 33 +- .../x/mongo/driver/operation/delete.toml | 47 - .../x/mongo/driver/operation/distinct.go | 13 +- .../x/mongo/driver/operation/distinct.toml | 37 - .../mongo/driver/operation/drop_collection.go | 17 +- .../driver/operation/drop_collection.toml | 21 - .../x/mongo/driver/operation/drop_database.go | 8 +- .../mongo/driver/operation/drop_database.toml | 18 - .../x/mongo/driver/operation/drop_indexes.go | 15 +- .../mongo/driver/operation/drop_indexes.toml | 28 - .../x/mongo/driver/operation/end_sessions.go | 10 +- .../mongo/driver/operation/end_sessions.toml | 16 - .../x/mongo/driver/operation/find.go | 24 +- .../x/mongo/driver/operation/find.toml | 105 -- .../mongo/driver/operation/find_and_modify.go | 40 +- .../driver/operation/find_and_modify.toml | 73 - .../operation/{ismaster.go => hello.go} | 212 ++- .../x/mongo/driver/operation/insert.go | 19 +- .../x/mongo/driver/operation/insert.toml | 45 - .../x/mongo/driver/operation/listDatabases.go | 61 +- .../mongo/driver/operation/listDatabases.toml | 37 - .../driver/operation/list_collections.go | 51 +- .../driver/operation/list_collections.toml | 23 - .../x/mongo/driver/operation/list_indexes.go | 8 +- .../mongo/driver/operation/list_indexes.toml | 20 - .../x/mongo/driver/operation/operation.go | 15 - .../x/mongo/driver/operation/update.go | 49 +- .../x/mongo/driver/operation/update.toml | 58 - .../x/mongo/driver/operation_legacy.go | 12 +- .../x/mongo/driver/session/client_session.go | 7 +- .../x/mongo/driver/session/server_session.go | 4 - .../x/mongo/driver/topology/connection.go | 169 +-- .../connection_legacy_command_metadata.go | 28 - .../driver/topology/connection_options.go | 78 +- .../x/mongo/driver/topology/errors.go | 17 +- .../x/mongo/driver/topology/fsm.go | 20 +- .../driver/topology/hanging_tls_conn_1_16.go | 37 + .../driver/topology/hanging_tls_conn_1_17.go | 44 + .../x/mongo/driver/topology/pool.go | 1300 ++++++++++++----- .../topology/pool_generation_counter.go | 23 +- .../x/mongo/driver/topology/resource_pool.go | 255 ---- .../x/mongo/driver/topology/rtt_monitor.go | 186 ++- .../x/mongo/driver/topology/server.go | 338 +++-- .../x/mongo/driver/topology/server_options.go | 116 +- .../topology/tls_connection_source_1_16.go | 58 + ...ource.go => tls_connection_source_1_17.go} | 15 +- .../x/mongo/driver/topology/topology.go | 131 +- .../mongo/driver/topology/topology_options.go | 47 +- .../driver/topology/topology_options_1_10.go | 9 - .../driver/topology/topology_options_1_9.go | 13 - .../mongo-driver/x/mongo/driver/uuid/uuid.go | 39 +- .../x/mongo/driver/wiremessage/wiremessage.go | 8 +- .../golang.org/x/sync/semaphore/semaphore.go | 136 -- vendor/modules.txt | 4 +- 184 files changed, 3805 insertions(+), 3451 deletions(-) delete mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bson_1_8.go create mode 100644 vendor/go.mongodb.org/mongo-driver/internal/randutil/randutil.go delete mode 100644 vendor/go.mongodb.org/mongo-driver/internal/semaphore.go delete mode 100644 vendor/go.mongodb.org/mongo-driver/mongo/options/clientoptions_1_10.go delete mode 100644 vendor/go.mongodb.org/mongo-driver/mongo/options/clientoptions_1_9.go delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/abort_transaction.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/aggregate.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/commit_transaction.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/count.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/create.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/createIndexes.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/delete.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/distinct.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_collection.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_database.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_indexes.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/end_sessions.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find_and_modify.toml rename vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/{ismaster.go => hello.go} (50%) delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/insert.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/listDatabases.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_collections.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_indexes.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/operation.go delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/update.toml delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/connection_legacy_command_metadata.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/hanging_tls_conn_1_16.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/hanging_tls_conn_1_17.go delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/resource_pool.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/tls_connection_source_1_16.go rename vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/{tls_connection_source.go => tls_connection_source_1_17.go} (66%) delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology_options_1_10.go delete mode 100644 vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology_options_1_9.go delete mode 100644 vendor/golang.org/x/sync/semaphore/semaphore.go diff --git a/go.mod b/go.mod index c6595d5f..dd99ce28 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,6 @@ require ( github.com/stretchr/testify v1.7.0 github.com/urfave/cli v1.22.5 github.com/vmihailenco/msgpack/v5 v5.3.5 - go.mongodb.org/mongo-driver v1.7.4 + go.mongodb.org/mongo-driver v1.9.1 golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf ) diff --git a/go.sum b/go.sum index 84e299ac..225be43b 100644 --- a/go.sum +++ b/go.sum @@ -388,8 +388,9 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.mongodb.org/mongo-driver v1.7.4 h1:sllcioag8Mec0LYkftYWq+cKNPIR4Kqq3iv9ZXY0g/E= go.mongodb.org/mongo-driver v1.7.4/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= +go.mongodb.org/mongo-driver v1.9.1 h1:m078y9v7sBItkt1aaoe2YlvWEXcD263e1a4E1fBrJ1c= +go.mongodb.org/mongo-driver v1.9.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -413,6 +414,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= @@ -580,6 +582,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bson.go b/vendor/go.mongodb.org/mongo-driver/bson/bson.go index 0c739dd0..95ffc107 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/bson.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/bson.go @@ -7,8 +7,6 @@ // Based on gopkg.in/mgo.v2/bson by Gustavo Niemeyer // See THIRD-PARTY-NOTICES for original license terms. -// +build go1.9 - package bson // import "go.mongodb.org/mongo-driver/bson" import ( diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bson_1_8.go b/vendor/go.mongodb.org/mongo-driver/bson/bson_1_8.go deleted file mode 100644 index bbe77928..00000000 --- a/vendor/go.mongodb.org/mongo-driver/bson/bson_1_8.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -// +build !go1.9 - -package bson // import "go.mongodb.org/mongo-driver/bson" - -import ( - "math" - "strconv" - "strings" -) - -// Zeroer allows custom struct types to implement a report of zero -// state. All struct types that don't implement Zeroer or where IsZero -// returns false are considered to be not zero. -type Zeroer interface { - IsZero() bool -} - -// D is an ordered representation of a BSON document. This type should be used when the order of the elements matters, -// such as MongoDB command documents. If the order of the elements does not matter, an M should be used instead. -// -// Example usage: -// -// bson.D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}} -type D []E - -// Map creates a map from the elements of the D. -func (d D) Map() M { - m := make(M, len(d)) - for _, e := range d { - m[e.Key] = e.Value - } - return m -} - -// E represents a BSON element for a D. It is usually used inside a D. -type E struct { - Key string - Value interface{} -} - -// M is an unordered representation of a BSON document. This type should be used when the order of the elements does not -// matter. This type is handled as a regular map[string]interface{} when encoding and decoding. Elements will be -// serialized in an undefined, random order. If the order of the elements matters, a D should be used instead. -// -// Example usage: -// -// bson.M{"foo": "bar", "hello": "world", "pi": 3.14159} -type M map[string]interface{} - -// An A is an ordered representation of a BSON array. -// -// Example usage: -// -// bson.A{"bar", "world", 3.14159, bson.D{{"qux", 12345}}} -type A []interface{} - -func formatDouble(f float64) string { - var s string - if math.IsInf(f, 1) { - s = "Infinity" - } else if math.IsInf(f, -1) { - s = "-Infinity" - } else if math.IsNaN(f) { - s = "NaN" - } else { - // Print exactly one decimalType place for integers; otherwise, print as many are necessary to - // perfectly represent it. - s = strconv.FormatFloat(f, 'G', -1, 64) - if !strings.ContainsRune(s, '.') { - s += ".0" - } - } - - return s -} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/bsoncodec.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/bsoncodec.go index 2c861b5c..96195bcc 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/bsoncodec.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/bsoncodec.go @@ -43,7 +43,7 @@ type Unmarshaler interface { } // ValueUnmarshaler is an interface implemented by types that can unmarshal a -// BSON value representaiton of themselves. The BSON bytes and type can be +// BSON value representation of themselves. The BSON bytes and type can be // assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it // wishes to retain the data after returning. type ValueUnmarshaler interface { diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_decoders.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_decoders.go index 32fd1427..20f4797d 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_decoders.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_decoders.go @@ -53,7 +53,7 @@ type DefaultValueDecoders struct{} // RegisterDefaultDecoders will register the decoder methods attached to DefaultValueDecoders with // the provided RegistryBuilder. // -// There is no support for decoding map[string]interface{} becuase there is no decoder for +// There is no support for decoding map[string]interface{} because there is no decoder for // interface{}, so users must either register this decoder themselves or use the // EmptyInterfaceDecoder available in the bson package. func (dvd DefaultValueDecoders) RegisterDefaultDecoders(rb *RegistryBuilder) { @@ -1504,6 +1504,18 @@ func (dvd DefaultValueDecoders) UnmarshalerDecodeValue(dc DecodeContext, vr bson return err } + // If the target Go value is a pointer and the BSON field value is empty, set the value to the + // zero value of the pointer (nil) and don't call UnmarshalBSON. UnmarshalBSON has no way to + // change the pointer value from within the function (only the value at the pointer address), + // so it can't set the pointer to "nil" itself. Since the most common Go value for an empty BSON + // field value is "nil", we set "nil" here and don't call UnmarshalBSON. This behavior matches + // the behavior of the Go "encoding/json" unmarshaler when the target Go value is a pointer and + // the JSON field value is "null". + if val.Kind() == reflect.Ptr && len(src) == 0 { + val.Set(reflect.Zero(val.Type())) + return nil + } + fn := val.Convert(tUnmarshaler).MethodByName("UnmarshalBSON") errVal := fn.Call([]reflect.Value{reflect.ValueOf(src)})[0] if !errVal.IsNil() { diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_encoders.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_encoders.go index 49a0c3f1..6bdb43cb 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_encoders.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_encoders.go @@ -30,7 +30,7 @@ var errInvalidValue = errors.New("cannot encode invalid element") var sliceWriterPool = sync.Pool{ New: func() interface{} { - sw := make(bsonrw.SliceWriter, 0, 0) + sw := make(bsonrw.SliceWriter, 0) return &sw }, } @@ -333,14 +333,7 @@ func (dve DefaultValueEncoders) mapEncodeValue(ec EncodeContext, dw bsonrw.Docum continue } - if enc, ok := currEncoder.(ValueEncoder); ok { - err = enc.EncodeValue(ec, vw, currVal) - if err != nil { - return err - } - continue - } - err = encoder.EncodeValue(ec, vw, currVal) + err = currEncoder.EncodeValue(ec, vw, currVal) if err != nil { return err } diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/map_codec.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/map_codec.go index fbb8ef42..1f7acbcf 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/map_codec.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/map_codec.go @@ -126,14 +126,7 @@ func (mc *MapCodec) mapEncodeValue(ec EncodeContext, dw bsonrw.DocumentWriter, v continue } - if enc, ok := currEncoder.(ValueEncoder); ok { - err = enc.EncodeValue(ec, vw, currVal) - if err != nil { - return err - } - continue - } - err = encoder.EncodeValue(ec, vw, currVal) + err = currEncoder.EncodeValue(ec, vw, currVal) if err != nil { return err } @@ -265,17 +258,15 @@ func (mc *MapCodec) decodeKey(key string, keyType reflect.Type) (reflect.Value, case reflect.String: keyVal = reflect.ValueOf(key).Convert(keyType) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - s := string(key) - n, parseErr := strconv.ParseInt(s, 10, 64) + n, parseErr := strconv.ParseInt(key, 10, 64) if parseErr != nil || reflect.Zero(keyType).OverflowInt(n) { - err = fmt.Errorf("failed to unmarshal number key %v", s) + err = fmt.Errorf("failed to unmarshal number key %v", key) } keyVal = reflect.ValueOf(n).Convert(keyType) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - s := string(key) - n, parseErr := strconv.ParseUint(s, 10, 64) + n, parseErr := strconv.ParseUint(key, 10, 64) if parseErr != nil || reflect.Zero(keyType).OverflowUint(n) { - err = fmt.Errorf("failed to unmarshal number key %v", s) + err = fmt.Errorf("failed to unmarshal number key %v", key) break } keyVal = reflect.ValueOf(n).Convert(keyType) diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go index 60abffb2..f6f3800d 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go @@ -54,12 +54,6 @@ func (entme ErrNoTypeMapEntry) Error() string { // ErrNotInterface is returned when the provided type is not an interface. var ErrNotInterface = errors.New("The provided type is not an interface") -var defaultRegistry *Registry - -func init() { - defaultRegistry = buildDefaultRegistry() -} - // A RegistryBuilder is used to build a Registry. This type is not goroutine // safe. type RegistryBuilder struct { @@ -304,7 +298,7 @@ func (rb *RegistryBuilder) Build() *Registry { return registry } -// LookupEncoder inspects the registry for an encoder for the given type. The lookup precendence works as follows: +// LookupEncoder inspects the registry for an encoder for the given type. The lookup precedence works as follows: // // 1. An encoder registered for the exact type. If the given type represents an interface, an encoder registered using // RegisterTypeEncoder for the interface will be selected. @@ -374,7 +368,7 @@ func (r *Registry) lookupInterfaceEncoder(t reflect.Type, allowAddr bool) (Value // in interfaceEncoders defaultEnc, found := r.lookupInterfaceEncoder(t, false) if !found { - defaultEnc, _ = r.kindEncoders[t.Kind()] + defaultEnc = r.kindEncoders[t.Kind()] } return newCondAddrEncoder(ienc.ve, defaultEnc), true } @@ -382,7 +376,7 @@ func (r *Registry) lookupInterfaceEncoder(t reflect.Type, allowAddr bool) (Value return nil, false } -// LookupDecoder inspects the registry for an decoder for the given type. The lookup precendence works as follows: +// LookupDecoder inspects the registry for an decoder for the given type. The lookup precedence works as follows: // // 1. A decoder registered for the exact type. If the given type represents an interface, a decoder registered using // RegisterTypeDecoder for the interface will be selected. @@ -445,7 +439,7 @@ func (r *Registry) lookupInterfaceDecoder(t reflect.Type, allowAddr bool) (Value // in interfaceDecoders defaultDec, found := r.lookupInterfaceDecoder(t, false) if !found { - defaultDec, _ = r.kindDecoders[t.Kind()] + defaultDec = r.kindDecoders[t.Kind()] } return newCondAddrDecoder(idec.vd, defaultDec), true } diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/struct_codec.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/struct_codec.go index 9ce90178..be3f2081 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/struct_codec.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/struct_codec.go @@ -331,14 +331,7 @@ func (sc *StructCodec) DecodeValue(r DecodeContext, vr bsonrw.ValueReader, val r return newDecodeError(fd.name, ErrNoDecoder{Type: field.Elem().Type()}) } - if decoder, ok := fd.decoder.(ValueDecoder); ok { - err = decoder.DecodeValue(dctx, vr, field.Elem()) - if err != nil { - return newDecodeError(fd.name, err) - } - continue - } - err = fd.decoder.DecodeValue(dctx, vr, field) + err = fd.decoder.DecodeValue(dctx, vr, field.Elem()) if err != nil { return newDecodeError(fd.name, err) } @@ -567,7 +560,7 @@ func (sc *StructCodec) describeStruct(r *Registry, t reflect.Type) (*structDescr } dominant, ok := dominantField(fields[i : i+advance]) if !ok || !sc.OverwriteDuplicatedInlinedFields { - return nil, fmt.Errorf("struct %s) duplicated key %s", t.String(), name) + return nil, fmt.Errorf("struct %s has duplicated key %s", t.String(), name) } sd.fl = append(sd.fl, dominant) sd.fm[name] = dominant diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/types.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/types.go index fb5b5108..07f4b70e 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/types.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/types.go @@ -16,36 +16,12 @@ import ( "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" ) -var ptBool = reflect.TypeOf((*bool)(nil)) -var ptInt8 = reflect.TypeOf((*int8)(nil)) -var ptInt16 = reflect.TypeOf((*int16)(nil)) -var ptInt32 = reflect.TypeOf((*int32)(nil)) -var ptInt64 = reflect.TypeOf((*int64)(nil)) -var ptInt = reflect.TypeOf((*int)(nil)) -var ptUint8 = reflect.TypeOf((*uint8)(nil)) -var ptUint16 = reflect.TypeOf((*uint16)(nil)) -var ptUint32 = reflect.TypeOf((*uint32)(nil)) -var ptUint64 = reflect.TypeOf((*uint64)(nil)) -var ptUint = reflect.TypeOf((*uint)(nil)) -var ptFloat32 = reflect.TypeOf((*float32)(nil)) -var ptFloat64 = reflect.TypeOf((*float64)(nil)) -var ptString = reflect.TypeOf((*string)(nil)) - var tBool = reflect.TypeOf(false) -var tFloat32 = reflect.TypeOf(float32(0)) var tFloat64 = reflect.TypeOf(float64(0)) -var tInt = reflect.TypeOf(int(0)) -var tInt8 = reflect.TypeOf(int8(0)) -var tInt16 = reflect.TypeOf(int16(0)) var tInt32 = reflect.TypeOf(int32(0)) var tInt64 = reflect.TypeOf(int64(0)) var tString = reflect.TypeOf("") var tTime = reflect.TypeOf(time.Time{}) -var tUint = reflect.TypeOf(uint(0)) -var tUint8 = reflect.TypeOf(uint8(0)) -var tUint16 = reflect.TypeOf(uint16(0)) -var tUint32 = reflect.TypeOf(uint32(0)) -var tUint64 = reflect.TypeOf(uint64(0)) var tEmpty = reflect.TypeOf((*interface{})(nil)).Elem() var tByteSlice = reflect.TypeOf([]byte(nil)) @@ -74,7 +50,6 @@ var tDecimal = reflect.TypeOf(primitive.Decimal128{}) var tMinKey = reflect.TypeOf(primitive.MinKey{}) var tMaxKey = reflect.TypeOf(primitive.MaxKey{}) var tD = reflect.TypeOf(primitive.D{}) -var tM = reflect.TypeOf(primitive.M{}) var tA = reflect.TypeOf(primitive.A{}) var tE = reflect.TypeOf(primitive.E{}) diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_parser.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_parser.go index 8a690e37..54c76bf7 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_parser.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_parser.go @@ -423,7 +423,7 @@ func (ejp *extJSONParser) readValue(t bsontype.Type) (*extJSONValue, error) { if ejp.canonical { return nil, invalidJSONErrorForType("object", t) } - return nil, invalidJSONErrorForType("ISO-8601 Internet Date/Time Format as decribed in RFC-3339", t) + return nil, invalidJSONErrorForType("ISO-8601 Internet Date/Time Format as described in RFC-3339", t) } ejp.advanceState() diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_wrappers.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_wrappers.go index 3af6fcf0..96957042 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_wrappers.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_wrappers.go @@ -19,7 +19,7 @@ import ( ) func wrapperKeyBSONType(key string) bsontype.Type { - switch string(key) { + switch key { case "$numberInt": return bsontype.Int32 case "$numberLong": @@ -269,7 +269,7 @@ func (ejv *extJSONValue) parseDouble() (float64, error) { return 0, fmt.Errorf("$numberDouble value should be string, but instead is %s", ejv.t) } - switch string(ejv.v.(string)) { + switch ejv.v.(string) { case "Infinity": return math.Inf(1), nil case "-Infinity": @@ -364,7 +364,7 @@ func (ejv *extJSONValue) parseRegex() (pattern, options string, err error) { for i, key := range regexObj.keys { val := regexObj.values[i] - switch string(key) { + switch key { case "pattern": if patFound { return "", "", errors.New("duplicate pattern key in $regularExpression") diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_writer.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_writer.go index 605e41a1..99ed524b 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_writer.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_writer.go @@ -10,7 +10,6 @@ import ( "bytes" "encoding/base64" "fmt" - "go.mongodb.org/mongo-driver/bson/primitive" "io" "math" "sort" @@ -19,13 +18,9 @@ import ( "sync" "time" "unicode/utf8" -) -var ejvwPool = sync.Pool{ - New: func() interface{} { - return new(extJSONValueWriter) - }, -} + "go.mongodb.org/mongo-driver/bson/primitive" +) // ExtJSONValueWriterPool is a pool for ExtJSON ValueWriters. type ExtJSONValueWriterPool struct { diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/value_reader.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/value_reader.go index 55378093..458588b6 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/value_reader.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/value_reader.go @@ -384,9 +384,13 @@ func (vr *valueReader) ReadBinary() (b []byte, btype byte, err error) { if err != nil { return nil, 0, err } + // Make a copy of the returned byte slice because it's just a subslice from the valueReader's + // buffer and is not safe to return in the unmarshaled value. + cp := make([]byte, len(b)) + copy(cp, b) vr.pop() - return b, btype, nil + return cp, btype, nil } func (vr *valueReader) ReadBoolean() (bool, error) { @@ -737,6 +741,9 @@ func (vr *valueReader) ReadValue() (ValueReader, error) { return vr, nil } +// readBytes reads length bytes from the valueReader starting at the current offset. Note that the +// returned byte slice is a subslice from the valueReader buffer and must be converted or copied +// before returning in an unmarshaled value. func (vr *valueReader) readBytes(length int32) ([]byte, error) { if length < 0 { return nil, fmt.Errorf("invalid length: %d", length) @@ -748,6 +755,7 @@ func (vr *valueReader) readBytes(length int32) ([]byte, error) { start := vr.offset vr.offset += int64(length) + return vr.d[start : start+int64(length)], nil } @@ -790,16 +798,6 @@ func (vr *valueReader) readCString() (string, error) { return string(vr.d[start : start+int64(idx)]), nil } -func (vr *valueReader) skipCString() error { - idx := bytes.IndexByte(vr.d[vr.offset:], 0x00) - if idx < 0 { - return io.EOF - } - // idx does not include the null byte - vr.offset += int64(idx) + 1 - return nil -} - func (vr *valueReader) readString() (string, error) { length, err := vr.readLength() if err != nil { diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/value_writer.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/value_writer.go index a39c4ea4..f95a08af 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/value_writer.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/value_writer.go @@ -529,7 +529,7 @@ func (vw *valueWriter) WriteDocumentEnd() error { vw.pop() if vw.stack[vw.frame].mode == mCodeWithScope { - // We ignore the error here because of the gaurantee of writeLength. + // We ignore the error here because of the guarantee of writeLength. // See the docs for writeLength for more info. _ = vw.writeLength() vw.pop() diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/writer.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/writer.go index 7644df12..dff65f87 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/writer.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/writer.go @@ -76,27 +76,3 @@ func (sw *SliceWriter) Write(p []byte) (int, error) { *sw = append(*sw, p...) return written, nil } - -type writer []byte - -func (w *writer) Write(p []byte) (int, error) { - index := len(*w) - return w.WriteAt(p, int64(index)) -} - -func (w *writer) WriteAt(p []byte, off int64) (int, error) { - newend := off + int64(len(p)) - if newend < int64(len(*w)) { - newend = int64(len(*w)) - } - - if newend > int64(cap(*w)) { - buf := make([]byte, int64(2*cap(*w))+newend) - copy(buf, *w) - *w = buf - } - - *w = []byte(*w)[:newend] - copy([]byte(*w)[off:], p) - return len(p), nil -} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsontype/bsontype.go b/vendor/go.mongodb.org/mongo-driver/bson/bsontype/bsontype.go index 63a59ca0..7c91ae51 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/bsontype/bsontype.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsontype/bsontype.go @@ -38,6 +38,8 @@ const ( BinaryUUIDOld byte = 0x03 BinaryUUID byte = 0x04 BinaryMD5 byte = 0x05 + BinaryEncrypted byte = 0x06 + BinaryColumn byte = 0x07 BinaryUserDefined byte = 0x80 ) diff --git a/vendor/go.mongodb.org/mongo-driver/bson/doc.go b/vendor/go.mongodb.org/mongo-driver/bson/doc.go index 094be934..5e3825a2 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/doc.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/doc.go @@ -118,7 +118,7 @@ // types, this tag is ignored. // // 3. truncate: If the truncate struct tag is specified on a field with a non-float numeric type, BSON doubles unmarshalled -// into that field will be trucated at the decimal point. For example, if 3.14 is unmarshalled into a field of type int, +// into that field will be truncated at the decimal point. For example, if 3.14 is unmarshalled into a field of type int, // it will be unmarshalled as 3. If this tag is not specified, the decoder will throw an error if the value cannot be // decoded without losing precision. For float64 or non-numeric types, this tag is ignored. // diff --git a/vendor/go.mongodb.org/mongo-driver/bson/marshal.go b/vendor/go.mongodb.org/mongo-driver/bson/marshal.go index 8d8fc8ce..db8d8ee9 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/marshal.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/marshal.go @@ -7,6 +7,9 @@ package bson import ( + "bytes" + "encoding/json" + "go.mongodb.org/mongo-driver/bson/bsoncodec" "go.mongodb.org/mongo-driver/bson/bsonrw" "go.mongodb.org/mongo-driver/bson/bsontype" @@ -221,3 +224,25 @@ func MarshalExtJSONAppendWithContext(ec bsoncodec.EncodeContext, dst []byte, val return *sw, nil } + +// IndentExtJSON will prefix and indent the provided extended JSON src and append it to dst. +func IndentExtJSON(dst *bytes.Buffer, src []byte, prefix, indent string) error { + return json.Indent(dst, src, prefix, indent) +} + +// MarshalExtJSONIndent returns the extended JSON encoding of val with each line with prefixed +// and indented. +func MarshalExtJSONIndent(val interface{}, canonical, escapeHTML bool, prefix, indent string) ([]byte, error) { + marshaled, err := MarshalExtJSON(val, canonical, escapeHTML) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + err = IndentExtJSON(&buf, marshaled, prefix, indent) + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/primitive/decimal.go b/vendor/go.mongodb.org/mongo-driver/bson/primitive/decimal.go index a57e1d69..ffe4eed0 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/primitive/decimal.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/primitive/decimal.go @@ -133,11 +133,9 @@ Loop: } // BigInt returns significand as big.Int and exponent, bi * 10 ^ exp. -func (d Decimal128) BigInt() (bi *big.Int, exp int, err error) { +func (d Decimal128) BigInt() (*big.Int, int, error) { high, low := d.GetBytes() - var posSign bool // positive sign - - posSign = high>>63&1 == 0 + posSign := high>>63&1 == 0 // positive sign switch high >> 58 & (1<<5 - 1) { case 0x1F: @@ -149,6 +147,7 @@ func (d Decimal128) BigInt() (bi *big.Int, exp int, err error) { return nil, 0, ErrParseNegInf } + var exp int if high>>61&3 == 3 { // Bits: 1*sign 2*ignored 14*exponent 111*significand. // Implicit 0b100 prefix in significand. @@ -171,7 +170,7 @@ func (d Decimal128) BigInt() (bi *big.Int, exp int, err error) { return new(big.Int), 0, nil } - bi = big.NewInt(0) + bi := big.NewInt(0) const host32bit = ^uint(0)>>32 == 0 if host32bit { bi.SetBits([]big.Word{big.Word(low), big.Word(low >> 32), big.Word(high), big.Word(high >> 32)}) @@ -182,7 +181,7 @@ func (d Decimal128) BigInt() (bi *big.Int, exp int, err error) { if !posSign { return bi.Neg(bi), exp, nil } - return + return bi, exp, nil } // IsNaN returns whether d is NaN. diff --git a/vendor/go.mongodb.org/mongo-driver/bson/primitive/objectid.go b/vendor/go.mongodb.org/mongo-driver/bson/primitive/objectid.go index a5a7d7c6..652898fe 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/primitive/objectid.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/primitive/objectid.go @@ -10,8 +10,8 @@ package primitive import ( - "bytes" "crypto/rand" + "encoding" "encoding/binary" "encoding/hex" "encoding/json" @@ -34,6 +34,9 @@ var NilObjectID ObjectID var objectIDCounter = readRandomUint32() var processUnique = processUniqueBytes() +var _ encoding.TextMarshaler = ObjectID{} +var _ encoding.TextUnmarshaler = &ObjectID{} + // NewObjectID generates a new ObjectID. func NewObjectID() ObjectID { return NewObjectIDFromTimestamp(time.Now()) @@ -67,7 +70,7 @@ func (id ObjectID) String() string { // IsZero returns true if id is the empty ObjectID. func (id ObjectID) IsZero() bool { - return bytes.Equal(id[:], NilObjectID[:]) + return id == NilObjectID } // ObjectIDFromHex creates a new ObjectID from a hex string. It returns an error if the hex string is not a @@ -83,7 +86,7 @@ func ObjectIDFromHex(s string) (ObjectID, error) { } var oid [12]byte - copy(oid[:], b[:]) + copy(oid[:], b) return oid, nil } @@ -94,6 +97,23 @@ func IsValidObjectID(s string) bool { return err == nil } +// MarshalText returns the ObjectID as UTF-8-encoded text. Implementing this allows us to use ObjectID +// as a map key when marshalling JSON. See https://pkg.go.dev/encoding#TextMarshaler +func (id ObjectID) MarshalText() ([]byte, error) { + return []byte(id.Hex()), nil +} + +// UnmarshalText populates the byte slice with the ObjectID. Implementing this allows us to use ObjectID +// as a map key when unmarshalling JSON. See https://pkg.go.dev/encoding#TextUnmarshaler +func (id *ObjectID) UnmarshalText(b []byte) error { + oid, err := ObjectIDFromHex(string(b)) + if err != nil { + return err + } + *id = oid + return nil +} + // MarshalJSON returns the ObjectID as a string func (id ObjectID) MarshalJSON() ([]byte, error) { return json.Marshal(id.Hex()) diff --git a/vendor/go.mongodb.org/mongo-driver/bson/primitive/primitive.go b/vendor/go.mongodb.org/mongo-driver/bson/primitive/primitive.go index 51984e22..b3cba1bf 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/primitive/primitive.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/primitive/primitive.go @@ -111,7 +111,7 @@ func (d DBPointer) String() string { // Equal compares d to d2 and returns true if they are equal. func (d DBPointer) Equal(d2 DBPointer) bool { - return d.DB == d2.DB && bytes.Equal(d.Pointer[:], d2.Pointer[:]) + return d == d2 } // IsZero returns if d is the empty DBPointer. diff --git a/vendor/go.mongodb.org/mongo-driver/bson/primitive_codecs.go b/vendor/go.mongodb.org/mongo-driver/bson/primitive_codecs.go index f397fa2d..1cbe3884 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/primitive_codecs.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/primitive_codecs.go @@ -14,6 +14,9 @@ import ( "go.mongodb.org/mongo-driver/bson/bsonrw" ) +var tRawValue = reflect.TypeOf(RawValue{}) +var tRaw = reflect.TypeOf(Raw(nil)) + var primitiveCodecs PrimitiveCodecs // PrimitiveCodecs is a namespace for all of the default bsoncodec.Codecs for the primitive types @@ -87,25 +90,3 @@ func (PrimitiveCodecs) RawDecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.Valu val.Set(reflect.ValueOf(rdr)) return err } - -func (pc PrimitiveCodecs) encodeRaw(ec bsoncodec.EncodeContext, dw bsonrw.DocumentWriter, raw Raw) error { - var copier bsonrw.Copier - elems, err := raw.Elements() - if err != nil { - return err - } - for _, elem := range elems { - dvw, err := dw.WriteDocumentElement(elem.Key()) - if err != nil { - return err - } - - val := elem.Value() - err = copier.CopyValueFromBytes(dvw, val.Type, val.Value) - if err != nil { - return err - } - } - - return dw.WriteDocumentEnd() -} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/raw.go b/vendor/go.mongodb.org/mongo-driver/bson/raw.go index 2aae9f56..efd705da 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/raw.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/raw.go @@ -15,7 +15,6 @@ import ( // ErrNilReader indicates that an operation was attempted on a nil bson.Reader. var ErrNilReader = errors.New("nil reader") -var errValidateDone = errors.New("validation loop complete") // Raw is a wrapper around a byte slice. It will interpret the slice as a // BSON document. This type is a wrapper around a bsoncore.Document. Errors returned from the @@ -84,9 +83,3 @@ func (r Raw) IndexErr(index uint) (RawElement, error) { // String implements the fmt.Stringer interface. func (r Raw) String() string { return bsoncore.Document(r).String() } - -// readi32 is a helper function for reading an int32 from slice of bytes. -func readi32(b []byte) int32 { - _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 - return int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24 -} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/registry.go b/vendor/go.mongodb.org/mongo-driver/bson/registry.go index 09062d20..16d7573e 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/registry.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/registry.go @@ -13,7 +13,7 @@ import "go.mongodb.org/mongo-driver/bson/bsoncodec" var DefaultRegistry = NewRegistryBuilder().Build() // NewRegistryBuilder creates a new RegistryBuilder configured with the default encoders and -// deocders from the bsoncodec.DefaultValueEncoders and bsoncodec.DefaultValueDecoders types and the +// decoders from the bsoncodec.DefaultValueEncoders and bsoncodec.DefaultValueDecoders types and the // PrimitiveCodecs type in this package. func NewRegistryBuilder() *bsoncodec.RegistryBuilder { rb := bsoncodec.NewRegistryBuilder() diff --git a/vendor/go.mongodb.org/mongo-driver/bson/types.go b/vendor/go.mongodb.org/mongo-driver/bson/types.go index bf91f691..13a1c35c 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/types.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/types.go @@ -7,11 +7,7 @@ package bson import ( - "reflect" - "time" - "go.mongodb.org/mongo-driver/bson/bsontype" - "go.mongodb.org/mongo-driver/bson/primitive" ) // These constants uniquely refer to each BSON type. @@ -38,48 +34,3 @@ const ( TypeMinKey = bsontype.MinKey TypeMaxKey = bsontype.MaxKey ) - -var tBinary = reflect.TypeOf(primitive.Binary{}) -var tBool = reflect.TypeOf(false) -var tCodeWithScope = reflect.TypeOf(primitive.CodeWithScope{}) -var tDBPointer = reflect.TypeOf(primitive.DBPointer{}) -var tDecimal = reflect.TypeOf(primitive.Decimal128{}) -var tD = reflect.TypeOf(D{}) -var tA = reflect.TypeOf(A{}) -var tDateTime = reflect.TypeOf(primitive.DateTime(0)) -var tUndefined = reflect.TypeOf(primitive.Undefined{}) -var tNull = reflect.TypeOf(primitive.Null{}) -var tRawValue = reflect.TypeOf(RawValue{}) -var tFloat32 = reflect.TypeOf(float32(0)) -var tFloat64 = reflect.TypeOf(float64(0)) -var tInt = reflect.TypeOf(int(0)) -var tInt8 = reflect.TypeOf(int8(0)) -var tInt16 = reflect.TypeOf(int16(0)) -var tInt32 = reflect.TypeOf(int32(0)) -var tInt64 = reflect.TypeOf(int64(0)) -var tJavaScript = reflect.TypeOf(primitive.JavaScript("")) -var tOID = reflect.TypeOf(primitive.ObjectID{}) -var tRaw = reflect.TypeOf(Raw(nil)) -var tRegex = reflect.TypeOf(primitive.Regex{}) -var tString = reflect.TypeOf("") -var tSymbol = reflect.TypeOf(primitive.Symbol("")) -var tTime = reflect.TypeOf(time.Time{}) -var tTimestamp = reflect.TypeOf(primitive.Timestamp{}) -var tUint = reflect.TypeOf(uint(0)) -var tUint8 = reflect.TypeOf(uint8(0)) -var tUint16 = reflect.TypeOf(uint16(0)) -var tUint32 = reflect.TypeOf(uint32(0)) -var tUint64 = reflect.TypeOf(uint64(0)) -var tMinKey = reflect.TypeOf(primitive.MinKey{}) -var tMaxKey = reflect.TypeOf(primitive.MaxKey{}) - -var tEmpty = reflect.TypeOf((*interface{})(nil)).Elem() -var tEmptySlice = reflect.TypeOf([]interface{}(nil)) - -var zeroVal reflect.Value - -// this references the quantity of milliseconds between zero time and -// the unix epoch. useful for making sure that we convert time.Time -// objects correctly to match the legacy bson library's handling of -// time.Time values. -const zeroEpochMs = int64(62135596800000) diff --git a/vendor/go.mongodb.org/mongo-driver/bson/unmarshal.go b/vendor/go.mongodb.org/mongo-driver/bson/unmarshal.go index 6f9ca04d..f936ba18 100644 --- a/vendor/go.mongodb.org/mongo-driver/bson/unmarshal.go +++ b/vendor/go.mongodb.org/mongo-driver/bson/unmarshal.go @@ -23,7 +23,7 @@ type Unmarshaler interface { } // ValueUnmarshaler is an interface implemented by types that can unmarshal a -// BSON value representaiton of themselves. The BSON bytes and type can be +// BSON value representation of themselves. The BSON bytes and type can be // assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it // wishes to retain the data after returning. type ValueUnmarshaler interface { diff --git a/vendor/go.mongodb.org/mongo-driver/event/monitoring.go b/vendor/go.mongodb.org/mongo-driver/event/monitoring.go index bc802f0f..ac05e401 100644 --- a/vendor/go.mongodb.org/mongo-driver/event/monitoring.go +++ b/vendor/go.mongodb.org/mongo-driver/event/monitoring.go @@ -22,6 +22,9 @@ type CommandStartedEvent struct { CommandName string RequestID int64 ConnectionID string + // ServerConnectionID contains the connection ID from the server of the operation. If the server does not return + // this value (e.g. on MDB < 4.2), it is unset. + ServerConnectionID *int32 // ServiceID contains the ID of the server to which the command was sent if it is running behind a load balancer. // Otherwise, it is unset. ServiceID *primitive.ObjectID @@ -33,6 +36,9 @@ type CommandFinishedEvent struct { CommandName string RequestID int64 ConnectionID string + // ServerConnectionID contains the connection ID from the server of the operation. If the server does not return + // this value (e.g. on MDB < 4.2), it is unset. + ServerConnectionID *int32 // ServiceID contains the ID of the server to which the command was sent if it is running behind a load balancer. // Otherwise, it is unset. ServiceID *primitive.ObjectID @@ -64,19 +70,22 @@ const ( ReasonStale = "stale" ReasonConnectionErrored = "connectionError" ReasonTimedOut = "timeout" + ReasonError = "error" ) // strings for pool command monitoring types const ( - ConnectionClosed = "ConnectionClosed" PoolCreated = "ConnectionPoolCreated" + PoolReady = "ConnectionPoolReady" + PoolCleared = "ConnectionPoolCleared" + PoolClosedEvent = "ConnectionPoolClosed" ConnectionCreated = "ConnectionCreated" ConnectionReady = "ConnectionReady" + ConnectionClosed = "ConnectionClosed" + GetStarted = "ConnectionCheckOutStarted" GetFailed = "ConnectionCheckOutFailed" GetSucceeded = "ConnectionCheckedOut" ConnectionReturned = "ConnectionCheckedIn" - PoolCleared = "ConnectionPoolCleared" - PoolClosedEvent = "ConnectionPoolClosed" ) // MonitorPoolOptions contains pool options as formatted in pool events diff --git a/vendor/go.mongodb.org/mongo-driver/internal/const.go b/vendor/go.mongodb.org/mongo-driver/internal/const.go index 772053ad..a7ef69d1 100644 --- a/vendor/go.mongodb.org/mongo-driver/internal/const.go +++ b/vendor/go.mongodb.org/mongo-driver/internal/const.go @@ -9,7 +9,11 @@ package internal // import "go.mongodb.org/mongo-driver/internal" // Version is the current version of the driver. var Version = "local build" -// SetMockServiceID enables a mode in which the driver mocks server support for returning a "serviceId" field in "hello" -// command responses by using the value of "topologyVersion.processId". This is used for testing load balancer support -// until an upstream service can support running behind a load balancer. -var SetMockServiceID = false +// LegacyHello is the legacy version of the hello command. +var LegacyHello = "isMaster" + +// LegacyHelloLowercase is the lowercase, legacy version of the hello command. +var LegacyHelloLowercase = "ismaster" + +// LegacyNotPrimary is the legacy version of the "not primary" server error message. +var LegacyNotPrimary = "not master" diff --git a/vendor/go.mongodb.org/mongo-driver/internal/randutil/randutil.go b/vendor/go.mongodb.org/mongo-driver/internal/randutil/randutil.go new file mode 100644 index 00000000..d7b753b7 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/internal/randutil/randutil.go @@ -0,0 +1,77 @@ +// Copyright (C) MongoDB, Inc. 2022-present. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + +// Package randutil provides common random number utilities. +package randutil + +import ( + crand "crypto/rand" + "fmt" + "io" + "math/rand" + "sync" +) + +// A LockedRand wraps a "math/rand".Rand and is safe to use from multiple goroutines. +type LockedRand struct { + mu sync.Mutex + r *rand.Rand +} + +// NewLockedRand returns a new LockedRand that uses random values from src to generate other random +// values. It is safe to use from multiple goroutines. +func NewLockedRand(src rand.Source) *LockedRand { + return &LockedRand{ + // Ignore gosec warning "Use of weak random number generator (math/rand instead of + // crypto/rand)". We intentionally use a pseudo-random number generator. + /* #nosec G404 */ + r: rand.New(src), + } +} + +// Read generates len(p) random bytes and writes them into p. It always returns len(p) and a nil +// error. +func (lr *LockedRand) Read(p []byte) (int, error) { + lr.mu.Lock() + n, err := lr.r.Read(p) + lr.mu.Unlock() + return n, err +} + +// Intn returns, as an int, a non-negative pseudo-random number in the half-open interval [0,n). It +// panics if n <= 0. +func (lr *LockedRand) Intn(n int) int { + lr.mu.Lock() + x := lr.r.Intn(n) + lr.mu.Unlock() + return x +} + +// Shuffle pseudo-randomizes the order of elements. n is the number of elements. Shuffle panics if +// n < 0. swap swaps the elements with indexes i and j. +// +// Note that Shuffle locks the LockedRand, so shuffling large collections may adversely affect other +// concurrent calls. If many concurrent Shuffle and random value calls are required, consider using +// the global "math/rand".Shuffle instead because it uses much more granular locking. +func (lr *LockedRand) Shuffle(n int, swap func(i, j int)) { + lr.mu.Lock() + lr.r.Shuffle(n, swap) + lr.mu.Unlock() +} + +// CryptoSeed returns a random int64 read from the "crypto/rand" random number generator. It is +// intended to be used to seed pseudorandom number generators at package initialization. It panics +// if it encounters any errors. +func CryptoSeed() int64 { + var b [8]byte + _, err := io.ReadFull(crand.Reader, b[:]) + if err != nil { + panic(fmt.Errorf("failed to read 8 bytes from a \"crypto/rand\".Reader: %v", err)) + } + + return (int64(b[0]) << 0) | (int64(b[1]) << 8) | (int64(b[2]) << 16) | (int64(b[3]) << 24) | + (int64(b[4]) << 32) | (int64(b[5]) << 40) | (int64(b[6]) << 48) | (int64(b[7]) << 56) +} diff --git a/vendor/go.mongodb.org/mongo-driver/internal/semaphore.go b/vendor/go.mongodb.org/mongo-driver/internal/semaphore.go deleted file mode 100644 index 792e531a..00000000 --- a/vendor/go.mongodb.org/mongo-driver/internal/semaphore.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package internal - -import ( - "context" - "errors" -) - -// NewSemaphore creates a new semaphore. -func NewSemaphore(slots uint64) *Semaphore { - ch := make(chan struct{}, slots) - for i := uint64(0); i < slots; i++ { - ch <- struct{}{} - } - - return &Semaphore{ - permits: ch, - } -} - -// Semaphore is a synchronization primitive that controls access -// to a common resource. -type Semaphore struct { - permits chan struct{} -} - -// Len gets the number of permits available. -func (s *Semaphore) Len() uint64 { - return uint64(len(s.permits)) -} - -// Wait waits until a resource is available or until the context -// is done. -func (s *Semaphore) Wait(ctx context.Context) error { - select { - case <-s.permits: - return nil - case <-ctx.Done(): - return ctx.Err() - } -} - -// Release releases a resource back into the pool. -func (s *Semaphore) Release() error { - select { - case s.permits <- struct{}{}: - default: - return errors.New("internal.Semaphore.Release: attempt to release more resources than are available") - } - - return nil -} diff --git a/vendor/go.mongodb.org/mongo-driver/internal/string_util.go b/vendor/go.mongodb.org/mongo-driver/internal/string_util.go index db1e1890..6cafa791 100644 --- a/vendor/go.mongodb.org/mongo-driver/internal/string_util.go +++ b/vendor/go.mongodb.org/mongo-driver/internal/string_util.go @@ -33,7 +33,7 @@ func StringSliceFromRawValue(name string, val bson.RawValue) ([]string, error) { return nil, err } - var strs []string + strs := make([]string, 0, len(arrayValues)) for _, arrayVal := range arrayValues { str, ok := arrayVal.StringValueOK() if !ok { diff --git a/vendor/go.mongodb.org/mongo-driver/internal/uri_validation_errors.go b/vendor/go.mongodb.org/mongo-driver/internal/uri_validation_errors.go index 8d1fcdc7..21e73002 100644 --- a/vendor/go.mongodb.org/mongo-driver/internal/uri_validation_errors.go +++ b/vendor/go.mongodb.org/mongo-driver/internal/uri_validation_errors.go @@ -15,4 +15,8 @@ var ( ErrLoadBalancedWithReplicaSet = errors.New("loadBalanced cannot be set to true if a replica set name is specified") // ErrLoadBalancedWithDirectConnection is returned when loadBalanced=true is specified in a URI with the directConnection option. ErrLoadBalancedWithDirectConnection = errors.New("loadBalanced cannot be set to true if the direct connection option is specified") + // ErrSRVMaxHostsWithReplicaSet is returned when srvMaxHosts > 0 is specified in a URI with the replicaSet option. + ErrSRVMaxHostsWithReplicaSet = errors.New("srvMaxHosts cannot be a positive value if a replica set name is specified") + // ErrSRVMaxHostsWithLoadBalanced is returned when srvMaxHosts > 0 is specified in a URI with loadBalanced=true. + ErrSRVMaxHostsWithLoadBalanced = errors.New("srvMaxHosts cannot be a positive value if loadBalanced is set to true") ) diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/bulk_write.go b/vendor/go.mongodb.org/mongo-driver/mongo/bulk_write.go index 56736a23..e748ced6 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/bulk_write.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/bulk_write.go @@ -35,6 +35,7 @@ type bulkWrite struct { selector description.ServerSelector writeConcern *writeconcern.WriteConcern result BulkWriteResult + let interface{} } func (bw *bulkWrite) execute(ctx context.Context) error { @@ -59,11 +60,6 @@ func (bw *bulkWrite) execute(ctx context.Context) error { continue } - bypassDocValidation := bw.bypassDocumentValidation - if bypassDocValidation != nil && !*bypassDocValidation { - bypassDocValidation = nil - } - batchRes, batchErr, err := bw.runBatch(ctx, batch) bw.mergeResults(batchRes) @@ -118,7 +114,7 @@ func (bw *bulkWrite) runBatch(ctx context.Context, batch bulkWriteBatch) (BulkWr batchErr.Labels = writeErr.Labels batchErr.WriteConcernError = convertDriverWriteConcernError(writeErr.WriteConcernError) } - batchRes.InsertedCount = int64(res.N) + batchRes.InsertedCount = res.N case *DeleteOneModel, *DeleteManyModel: res, err := bw.runDelete(ctx, batch) if err != nil { @@ -130,7 +126,7 @@ func (bw *bulkWrite) runBatch(ctx context.Context, batch bulkWriteBatch) (BulkWr batchErr.Labels = writeErr.Labels batchErr.WriteConcernError = convertDriverWriteConcernError(writeErr.WriteConcernError) } - batchRes.DeletedCount = int64(res.N) + batchRes.DeletedCount = res.N case *ReplaceOneModel, *UpdateOneModel, *UpdateManyModel: res, err := bw.runUpdate(ctx, batch) if err != nil { @@ -142,8 +138,8 @@ func (bw *bulkWrite) runBatch(ctx context.Context, batch bulkWriteBatch) (BulkWr batchErr.Labels = writeErr.Labels batchErr.WriteConcernError = convertDriverWriteConcernError(writeErr.WriteConcernError) } - batchRes.MatchedCount = int64(res.N) - batchRes.ModifiedCount = int64(res.NModified) + batchRes.MatchedCount = res.N + batchRes.ModifiedCount = res.NModified batchRes.UpsertedCount = int64(len(res.Upserted)) for _, upsert := range res.Upserted { batchRes.UpsertedIDs[int64(batch.indexes[upsert.Index])] = upsert.ID @@ -233,6 +229,13 @@ func (bw *bulkWrite) runDelete(ctx context.Context, batch bulkWriteBatch) (opera Database(bw.collection.db.name).Collection(bw.collection.name). Deployment(bw.collection.client.deployment).Crypt(bw.collection.client.cryptFLE).Hint(hasHint). ServerAPI(bw.collection.client.serverAPI) + if bw.let != nil { + let, err := transformBsoncoreDocument(bw.collection.registry, bw.let, true, "let") + if err != nil { + return operation.DeleteResult{}, err + } + op = op.Let(let) + } if bw.ordered != nil { op = op.Ordered(*bw.ordered) } @@ -314,6 +317,13 @@ func (bw *bulkWrite) runUpdate(ctx context.Context, batch bulkWriteBatch) (opera Database(bw.collection.db.name).Collection(bw.collection.name). Deployment(bw.collection.client.deployment).Crypt(bw.collection.client.cryptFLE).Hint(hasHint). ArrayFilters(hasArrayFilters).ServerAPI(bw.collection.client.serverAPI) + if bw.let != nil { + let, err := transformBsoncoreDocument(bw.collection.registry, bw.let, true, "let") + if err != nil { + return operation.UpdateResult{}, err + } + op = op.Let(let) + } if bw.ordered != nil { op = op.Ordered(*bw.ordered) } diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/change_stream.go b/vendor/go.mongodb.org/mongo-driver/mongo/change_stream.go index c10c0396..a76eb7c9 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/change_stream.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/change_stream.go @@ -38,7 +38,7 @@ var ( resumableErrorLabel = "ResumableChangeStreamError" errorCursorNotFound int32 = 43 // CursorNotFound error code - // Whitelist of error codes that are considered resumable. + // Allowlist of error codes that are considered resumable. resumableChangeStreamErrors = map[int32]struct{}{ 6: {}, // HostUnreachable 7: {}, // HostNotFound @@ -47,11 +47,11 @@ var ( 189: {}, // PrimarySteppedDown 262: {}, // ExceededTimeLimit 9001: {}, // SocketException - 10107: {}, // NotMaster + 10107: {}, // NotPrimary 11600: {}, // InterruptedAtShutdown 11602: {}, // InterruptedDueToReplStateChange - 13435: {}, // NotMasterNoSlaveOk - 13436: {}, // NotMasterOrSecondary + 13435: {}, // NotPrimaryNoSecondaryOK + 13436: {}, // NotPrimaryOrSecondary 63: {}, // StaleShardVersion 150: {}, // StaleEpoch 13388: {}, // StaleConfig @@ -69,21 +69,22 @@ type ChangeStream struct { // TryNext. If continued access is required, a copy must be made. Current bson.Raw - aggregate *operation.Aggregate - pipelineSlice []bsoncore.Document - cursor changeStreamCursor - cursorOptions driver.CursorOptions - batch []bsoncore.Document - resumeToken bson.Raw - err error - sess *session.Client - client *Client - registry *bsoncodec.Registry - streamType StreamType - options *options.ChangeStreamOptions - selector description.ServerSelector - operationTime *primitive.Timestamp - wireVersion *description.VersionRange + aggregate *operation.Aggregate + pipelineSlice []bsoncore.Document + pipelineOptions map[string]bsoncore.Value + cursor changeStreamCursor + cursorOptions driver.CursorOptions + batch []bsoncore.Document + resumeToken bson.Raw + err error + sess *session.Client + client *Client + registry *bsoncodec.Registry + streamType StreamType + options *options.ChangeStreamOptions + selector description.ServerSelector + operationTime *primitive.Timestamp + wireVersion *description.VersionRange } type changeStreamConfig struct { @@ -94,7 +95,7 @@ type changeStreamConfig struct { streamType StreamType collectionName string databaseName string - crypt *driver.Crypt + crypt driver.Crypt } func newChangeStream(ctx context.Context, config changeStreamConfig, pipeline interface{}, @@ -141,7 +142,38 @@ func newChangeStream(ctx context.Context, config changeStreamConfig, pipeline in cs.cursorOptions.BatchSize = *cs.options.BatchSize } if cs.options.MaxAwaitTime != nil { - cs.cursorOptions.MaxTimeMS = int64(time.Duration(*cs.options.MaxAwaitTime) / time.Millisecond) + cs.cursorOptions.MaxTimeMS = int64(*cs.options.MaxAwaitTime / time.Millisecond) + } + if cs.options.Custom != nil { + // Marshal all custom options before passing to the initial aggregate. Return + // any errors from Marshaling. + customOptions := make(map[string]bsoncore.Value) + for optionName, optionValue := range cs.options.Custom { + bsonType, bsonData, err := bson.MarshalValueWithRegistry(cs.registry, optionValue) + if err != nil { + cs.err = err + closeImplicitSession(cs.sess) + return nil, cs.Err() + } + optionValueBSON := bsoncore.Value{Type: bsonType, Data: bsonData} + customOptions[optionName] = optionValueBSON + } + cs.aggregate.CustomOptions(customOptions) + } + if cs.options.CustomPipeline != nil { + // Marshal all custom pipeline options before building pipeline slice. Return + // any errors from Marshaling. + cs.pipelineOptions = make(map[string]bsoncore.Value) + for optionName, optionValue := range cs.options.CustomPipeline { + bsonType, bsonData, err := bson.MarshalValueWithRegistry(cs.registry, optionValue) + if err != nil { + cs.err = err + closeImplicitSession(cs.sess) + return nil, cs.Err() + } + optionValueBSON := bsoncore.Value{Type: bsonType, Data: bsonData} + cs.pipelineOptions[optionName] = optionValueBSON + } } switch cs.streamType { @@ -212,7 +244,7 @@ func (cs *ChangeStream) executeOperation(ctx context.Context, resuming bool) err cs.aggregate.Deployment(cs.createOperationDeployment(server, conn)) if resuming { - cs.replaceOptions(ctx, cs.wireVersion) + cs.replaceOptions(cs.wireVersion) csOptDoc := cs.createPipelineOptionsDoc() pipIdx, pipDoc := bsoncore.AppendDocumentStart(nil) @@ -390,6 +422,11 @@ func (cs *ChangeStream) createPipelineOptionsDoc() bsoncore.Document { plDoc = bsoncore.AppendTimestampElement(plDoc, "startAtOperationTime", cs.options.StartAtOperationTime.T, cs.options.StartAtOperationTime.I) } + // Append custom pipeline options. + for optionName, optionValue := range cs.pipelineOptions { + plDoc = bsoncore.AppendValueElement(plDoc, optionName, optionValue) + } + if plDoc, cs.err = bsoncore.AppendDocumentEnd(plDoc, plDocIdx); cs.err != nil { return nil } @@ -408,7 +445,7 @@ func (cs *ChangeStream) pipelineToBSON() (bsoncore.Document, error) { return pipelineArr, cs.err } -func (cs *ChangeStream) replaceOptions(ctx context.Context, wireVersion *description.VersionRange) { +func (cs *ChangeStream) replaceOptions(wireVersion *description.VersionRange) { // Cached resume token: use the resume token as the resumeAfter option and set no other resume options if cs.resumeToken != nil { cs.options.SetResumeAfter(cs.resumeToken) @@ -604,7 +641,7 @@ func (cs *ChangeStream) isResumableError() bool { return commandErr.HasErrorLabel(resumableErrorLabel) } - // For wire versions below 9, a server error is resumable if its code is on the whitelist. + // For wire versions below 9, a server error is resumable if its code is on the allowlist. _, resumable := resumableChangeStreamErrors[commandErr.Code] return resumable } diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/change_stream_deployment.go b/vendor/go.mongodb.org/mongo-driver/mongo/change_stream_deployment.go index 5ff82c5f..36c6e254 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/change_stream_deployment.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/change_stream_deployment.go @@ -8,6 +8,7 @@ package mongo import ( "context" + "time" "go.mongodb.org/mongo-driver/mongo/description" "go.mongodb.org/mongo-driver/x/mongo/driver" @@ -35,6 +36,10 @@ func (c *changeStreamDeployment) Connection(context.Context) (driver.Connection, return c.conn, nil } +func (c *changeStreamDeployment) MinRTT() time.Duration { + return c.server.MinRTT() +} + func (c *changeStreamDeployment) ProcessError(err error, conn driver.Connection) driver.ProcessErrorResult { ep, ok := c.server.(driver.ErrorProcessor) if !ok { diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/client.go b/vendor/go.mongodb.org/mongo-driver/mongo/client.go index 4f429207..ddc08bd5 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/client.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/client.go @@ -25,7 +25,6 @@ import ( "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" "go.mongodb.org/mongo-driver/x/mongo/driver" "go.mongodb.org/mongo-driver/x/mongo/driver/auth" - "go.mongodb.org/mongo-driver/x/mongo/driver/connstring" "go.mongodb.org/mongo-driver/x/mongo/driver/ocsp" "go.mongodb.org/mongo-driver/x/mongo/driver/operation" "go.mongodb.org/mongo-driver/x/mongo/driver/session" @@ -52,7 +51,6 @@ type Client struct { id uuid.UUID topologyOptions []topology.Option deployment driver.Deployment - connString connstring.ConnString localThreshold time.Duration retryWrites bool retryReads bool @@ -61,7 +59,6 @@ type Client struct { readConcern *readconcern.ReadConcern writeConcern *writeconcern.WriteConcern registry *bsoncodec.Registry - marshaller BSONAppender monitor *event.CommandMonitor serverAPI *driver.ServerAPIOptions serverMonitor *event.ServerMonitor @@ -71,7 +68,7 @@ type Client struct { keyVaultClientFLE *Client keyVaultCollFLE *Collection mongocryptdFLE *mcryptClient - cryptFLE *driver.Crypt + cryptFLE driver.Crypt metadataClientFLE *Client internalClientFLE *Client } @@ -375,10 +372,22 @@ func (c *Client) configure(opts *options.ClientOptions) error { // ClusterClock c.clock = new(session.ClusterClock) - // Pass down URI so topology can determine whether or not SRV polling is required - topologyOpts = append(topologyOpts, topology.WithURI(func(uri string) string { - return opts.GetURI() - })) + // Pass down URI, SRV service name, and SRV max hosts so topology can poll SRV records correctly. + topologyOpts = append(topologyOpts, + topology.WithURI(func(uri string) string { return opts.GetURI() }), + topology.WithSRVServiceName(func(srvName string) string { + if opts.SRVServiceName != nil { + return *opts.SRVServiceName + } + return "" + }), + topology.WithSRVMaxHosts(func(srvMaxHosts int) int { + if opts.SRVMaxHosts != nil { + return *opts.SRVMaxHosts + } + return 0 + }), + ) // AppName var appName string @@ -425,7 +434,7 @@ func (c *Client) configure(opts *options.ClientOptions) error { // Handshaker var handshaker = func(driver.Handshaker) driver.Handshaker { - return operation.NewIsMaster().AppName(appName).Compressors(comps).ClusterClock(c.clock). + return operation.NewHello().AppName(appName).Compressors(comps).ClusterClock(c.clock). ServerAPI(c.serverAPI).LoadBalanced(loadBalanced) } // Auth & Database & Password & Username @@ -537,6 +546,13 @@ func (c *Client) configure(opts *options.ClientOptions) error { topology.WithMinConnections(func(uint64) uint64 { return *opts.MinPoolSize }), ) } + // MaxConnecting + if opts.MaxConnecting != nil { + serverOpts = append( + serverOpts, + topology.WithMaxConnecting(func(uint64) uint64 { return *opts.MaxConnecting }), + ) + } // PoolMonitor if opts.PoolMonitor != nil { serverOpts = append( @@ -625,6 +641,8 @@ func (c *Client) configure(opts *options.ClientOptions) error { if err := c.configureAutoEncryption(opts); err != nil { return err } + } else { + c.cryptFLE = opts.Crypt } // OCSP cache @@ -669,9 +687,9 @@ func (c *Client) configure(opts *options.ClientOptions) error { // Deployment if opts.Deployment != nil { - // topology options: WithSeedlist and WithURI + // topology options: WithSeedlist, WithURI, WithSRVServiceName and WithSRVMaxHosts // server options: WithClock and WithConnectionOptions - if len(serverOpts) > 2 || len(topologyOpts) > 2 { + if len(serverOpts) > 2 || len(topologyOpts) > 4 { return errors.New("cannot specify topology or server options with a deployment") } c.deployment = opts.Deployment @@ -779,11 +797,13 @@ func (c *Client) configureCryptFLE(opts *options.AutoEncryptionOptions) error { if !bypass { cir = collInfoRetriever{client: c.metadataClientFLE} } + cryptOpts := &driver.CryptOptions{ CollInfoFn: cir.cryptCollInfo, KeyFn: kr.cryptKeys, MarkFn: c.mongocryptdFLE.markCommand, KmsProviders: kmsProviders, + TLSConfig: opts.TLSConfig, BypassAutoEncryption: bypass, SchemaMap: cryptSchemaMap, } @@ -823,7 +843,7 @@ func (c *Client) Database(name string, opts ...*options.DatabaseOptions) *Databa // databases are included in the result. It cannot be nil. An empty document (e.g. bson.D{}) should be used to include // all databases. // -// The opts paramter can be used to specify options for this operation (see the options.ListDatabasesOptions documentation). +// The opts parameter can be used to specify options for this operation (see the options.ListDatabasesOptions documentation). // // For more information about the command, see https://docs.mongodb.com/manual/reference/command/listDatabases/. func (c *Client) ListDatabases(ctx context.Context, filter interface{}, opts ...*options.ListDatabasesOptions) (ListDatabasesResult, error) { @@ -834,6 +854,9 @@ func (c *Client) ListDatabases(ctx context.Context, filter interface{}, opts ... sess := sessionFromContext(ctx) err := c.validSession(sess) + if err != nil { + return ListDatabasesResult{}, err + } if sess == nil && c.sessionPool != nil { sess, err = session.NewClientSession(c.sessionPool, c.id, session.Implicit) if err != nil { diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/client_encryption.go b/vendor/go.mongodb.org/mongo-driver/mongo/client_encryption.go index 4b1f12d3..fe4646b6 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/client_encryption.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/client_encryption.go @@ -22,7 +22,7 @@ import ( // ClientEncryption is used to create data keys and explicitly encrypt and decrypt BSON values. type ClientEncryption struct { - crypt *driver.Crypt + crypt driver.Crypt keyVaultClient *Client keyVaultColl *Collection } @@ -54,6 +54,7 @@ func NewClientEncryption(keyVaultClient *Client, opts ...*options.ClientEncrypti KeyFn: kr.cryptKeys, CollInfoFn: cir.cryptCollInfo, KmsProviders: kmsProviders, + TLSConfig: ceo.TLSConfig, }) if err != nil { return nil, err diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/collection.go b/vendor/go.mongodb.org/mongo-driver/mongo/collection.go index 02e197d5..590d9280 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/collection.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/collection.go @@ -225,6 +225,7 @@ func (coll *Collection) BulkWrite(ctx context.Context, models []WriteModel, collection: coll, selector: selector, writeConcern: wc, + let: bwo.Let, } err = op.execute(ctx) @@ -454,6 +455,13 @@ func (coll *Collection) delete(ctx context.Context, filter interface{}, deleteOn if do.Hint != nil { op = op.Hint(true) } + if do.Let != nil { + let, err := transformBsoncoreDocument(coll.registry, do.Let, true, "let") + if err != nil { + return nil, err + } + op = op.Let(let) + } // deleteMany cannot be retried retryMode := driver.RetryNone @@ -465,7 +473,7 @@ func (coll *Collection) delete(ctx context.Context, filter interface{}, deleteOn if rr&expectedRr == 0 { return nil, err } - return &DeleteResult{DeletedCount: int64(op.Result().N)}, err + return &DeleteResult{DeletedCount: op.Result().N}, err } // DeleteOne executes a delete command to delete at most one document from the collection. @@ -548,6 +556,13 @@ func (coll *Collection) updateOrReplace(ctx context.Context, filter bsoncore.Doc Database(coll.db.name).Collection(coll.name). Deployment(coll.client.deployment).Crypt(coll.client.cryptFLE).Hint(uo.Hint != nil). ArrayFilters(uo.ArrayFilters != nil).Ordered(true).ServerAPI(coll.client.serverAPI) + if uo.Let != nil { + let, err := transformBsoncoreDocument(coll.registry, uo.Let, true, "let") + if err != nil { + return nil, err + } + op = op.Let(let) + } if uo.BypassDocumentValidation != nil && *uo.BypassDocumentValidation { op = op.BypassDocumentValidation(*uo.BypassDocumentValidation) @@ -567,8 +582,8 @@ func (coll *Collection) updateOrReplace(ctx context.Context, filter bsoncore.Doc opRes := op.Result() res := &UpdateResult{ - MatchedCount: int64(opRes.N), - ModifiedCount: int64(opRes.NModified), + MatchedCount: opRes.N, + ModifiedCount: opRes.NModified, UpsertedCount: int64(len(opRes.Upserted)), } if len(opRes.Upserted) > 0 { @@ -693,11 +708,15 @@ func (coll *Collection) ReplaceOne(ctx context.Context, filter interface{}, updateOptions := make([]*options.UpdateOptions, 0, len(opts)) for _, opt := range opts { + if opt == nil { + continue + } uOpts := options.Update() uOpts.BypassDocumentValidation = opt.BypassDocumentValidation uOpts.Collation = opt.Collation uOpts.Upsert = opt.Upsert uOpts.Hint = opt.Hint + uOpts.Let = opt.Let updateOptions = append(updateOptions, uOpts) } @@ -736,8 +755,7 @@ func (coll *Collection) Aggregate(ctx context.Context, pipeline interface{}, } // aggreate is the helper method for Aggregate -func aggregate(a aggregateParams) (*Cursor, error) { - +func aggregate(a aggregateParams) (cur *Cursor, err error) { if a.ctx == nil { a.ctx = context.Background() } @@ -748,6 +766,12 @@ func aggregate(a aggregateParams) (*Cursor, error) { } sess := sessionFromContext(a.ctx) + // Always close any created implicit sessions if aggregate returns an error. + defer func() { + if err != nil && sess != nil { + closeImplicitSession(sess) + } + }() if sess == nil && a.client.sessionPool != nil { sess, err = session.NewClientSession(a.client.sessionPool, a.client.id, session.Implicit) if err != nil { @@ -772,9 +796,9 @@ func aggregate(a aggregateParams) (*Cursor, error) { sess = nil } - selector := makePinnedSelector(sess, a.writeSelector) - if !hasOutputStage { - selector = makeReadPrefSelector(sess, a.readSelector, a.client.localThreshold) + selector := makeReadPrefSelector(sess, a.readSelector, a.client.localThreshold) + if hasOutputStage { + selector = makeOutputAggregateSelector(sess, a.readPreference, a.client.localThreshold) } ao := options.MergeAggregateOptions(a.opts...) @@ -784,6 +808,7 @@ func aggregate(a aggregateParams) (*Cursor, error) { Session(sess). WriteConcern(wc). ReadConcern(rc). + ReadPreference(a.readPreference). CommandMonitor(a.client.monitor). ServerSelector(selector). ClusterClock(a.client.clock). @@ -791,13 +816,8 @@ func aggregate(a aggregateParams) (*Cursor, error) { Collection(a.col). Deployment(a.client.deployment). Crypt(a.client.cryptFLE). - ServerAPI(a.client.serverAPI) - if !hasOutputStage { - // Only pass the user-specified read preference if the aggregation doesn't have a $out or $merge stage. - // Otherwise, the read preference could be forwarded to a mongos, which would error if the aggregation were - // executed against a non-primary node. - op.ReadPreference(a.readPreference) - } + ServerAPI(a.client.serverAPI). + HasOutputStage(hasOutputStage) if ao.AllowDiskUse != nil { op.AllowDiskUse(*ao.AllowDiskUse) @@ -825,7 +845,6 @@ func aggregate(a aggregateParams) (*Cursor, error) { if ao.Hint != nil { hintVal, err := transformValue(a.registry, ao.Hint, false, "hint") if err != nil { - closeImplicitSession(sess) return nil, err } op.Hint(hintVal) @@ -833,11 +852,24 @@ func aggregate(a aggregateParams) (*Cursor, error) { if ao.Let != nil { let, err := transformBsoncoreDocument(a.registry, ao.Let, true, "let") if err != nil { - closeImplicitSession(sess) return nil, err } op.Let(let) } + if ao.Custom != nil { + // Marshal all custom options before passing to the aggregate operation. Return + // any errors from Marshaling. + customOptions := make(map[string]bsoncore.Value) + for optionName, optionValue := range ao.Custom { + bsonType, bsonData, err := bson.MarshalValueWithRegistry(a.registry, optionValue) + if err != nil { + return nil, err + } + optionValueBSON := bsoncore.Value{Type: bsonType, Data: bsonData} + customOptions[optionName] = optionValueBSON + } + op.CustomOptions(customOptions) + } retry := driver.RetryNone if a.retryRead && !hasOutputStage { @@ -847,7 +879,6 @@ func aggregate(a aggregateParams) (*Cursor, error) { err = op.Execute(a.ctx) if err != nil { - closeImplicitSession(sess) if wce, ok := err.(driver.WriteCommandError); ok && wce.WriteConcernError != nil { return nil, *convertDriverWriteConcernError(wce.WriteConcernError) } @@ -856,7 +887,6 @@ func aggregate(a aggregateParams) (*Cursor, error) { bc, err := op.Result(cursorOpts) if err != nil { - closeImplicitSession(sess) return nil, replaceErrors(err) } cursor, err := newCursorWithSession(bc, a.registry, sess) @@ -1049,7 +1079,7 @@ func (coll *Collection) Distinct(ctx context.Context, fieldName string, filter i selector := makeReadPrefSelector(sess, coll.readSelector, coll.client.localThreshold) option := options.MergeDistinctOptions(opts...) - op := operation.NewDistinct(fieldName, bsoncore.Document(f)). + op := operation.NewDistinct(fieldName, f). Session(sess).ClusterClock(coll.client.clock). Database(coll.db.name).Collection(coll.name).CommandMonitor(coll.client.monitor). Deployment(coll.client.deployment).ReadConcern(rc).ReadPreference(coll.readPreference). @@ -1104,7 +1134,7 @@ func (coll *Collection) Distinct(ctx context.Context, fieldName string, filter i // // For more information about the command, see https://docs.mongodb.com/manual/reference/command/find/. func (coll *Collection) Find(ctx context.Context, filter interface{}, - opts ...*options.FindOptions) (*Cursor, error) { + opts ...*options.FindOptions) (cur *Cursor, err error) { if ctx == nil { ctx = context.Background() @@ -1116,6 +1146,12 @@ func (coll *Collection) Find(ctx context.Context, filter interface{}, } sess := sessionFromContext(ctx) + // Always close any created implicit sessions if Find returns an error. + defer func() { + if err != nil && sess != nil { + closeImplicitSession(sess) + } + }() if sess == nil && coll.client.sessionPool != nil { var err error sess, err = session.NewClientSession(coll.client.sessionPool, coll.client.id, session.Implicit) @@ -1126,7 +1162,6 @@ func (coll *Collection) Find(ctx context.Context, filter interface{}, err = coll.client.validSession(sess) if err != nil { - closeImplicitSession(sess) return nil, err } @@ -1173,11 +1208,17 @@ func (coll *Collection) Find(ctx context.Context, filter interface{}, if fo.Hint != nil { hint, err := transformValue(coll.registry, fo.Hint, false, "hint") if err != nil { - closeImplicitSession(sess) return nil, err } op.Hint(hint) } + if fo.Let != nil { + let, err := transformBsoncoreDocument(coll.registry, fo.Let, true, "let") + if err != nil { + return nil, err + } + op.Let(let) + } if fo.Limit != nil { limit := *fo.Limit if limit < 0 { @@ -1190,7 +1231,6 @@ func (coll *Collection) Find(ctx context.Context, filter interface{}, if fo.Max != nil { max, err := transformBsoncoreDocument(coll.registry, fo.Max, true, "max") if err != nil { - closeImplicitSession(sess) return nil, err } op.Max(max) @@ -1204,7 +1244,6 @@ func (coll *Collection) Find(ctx context.Context, filter interface{}, if fo.Min != nil { min, err := transformBsoncoreDocument(coll.registry, fo.Min, true, "min") if err != nil { - closeImplicitSession(sess) return nil, err } op.Min(min) @@ -1218,7 +1257,6 @@ func (coll *Collection) Find(ctx context.Context, filter interface{}, if fo.Projection != nil { proj, err := transformBsoncoreDocument(coll.registry, fo.Projection, true, "projection") if err != nil { - closeImplicitSession(sess) return nil, err } op.Projection(proj) @@ -1238,7 +1276,6 @@ func (coll *Collection) Find(ctx context.Context, filter interface{}, if fo.Sort != nil { sort, err := transformBsoncoreDocument(coll.registry, fo.Sort, false, "sort") if err != nil { - closeImplicitSession(sess) return nil, err } op.Sort(sort) @@ -1250,13 +1287,11 @@ func (coll *Collection) Find(ctx context.Context, filter interface{}, op = op.Retry(retry) if err = op.Execute(ctx); err != nil { - closeImplicitSession(sess) return nil, replaceErrors(err) } bc, err := op.Result(cursorOpts) if err != nil { - closeImplicitSession(sess) return nil, replaceErrors(err) } return newCursorWithSession(bc, coll.registry, sess) @@ -1278,9 +1313,12 @@ func (coll *Collection) FindOne(ctx context.Context, filter interface{}, ctx = context.Background() } - findOpts := make([]*options.FindOptions, len(opts)) - for i, opt := range opts { - findOpts[i] = &options.FindOptions{ + findOpts := make([]*options.FindOptions, 0, len(opts)) + for _, opt := range opts { + if opt == nil { + continue + } + findOpts = append(findOpts, &options.FindOptions{ AllowPartialResults: opt.AllowPartialResults, BatchSize: opt.BatchSize, Collation: opt.Collation, @@ -1299,7 +1337,7 @@ func (coll *Collection) FindOne(ctx context.Context, filter interface{}, Skip: opt.Skip, Snapshot: opt.Snapshot, Sort: opt.Sort, - } + }) } // Unconditionally send a limit to make sure only one document is returned and the cursor is not kept open // by the server. @@ -1410,6 +1448,13 @@ func (coll *Collection) FindOneAndDelete(ctx context.Context, filter interface{} } op = op.Hint(hint) } + if fod.Let != nil { + let, err := transformBsoncoreDocument(coll.registry, fod.Let, true, "let") + if err != nil { + return &SingleResult{err: err} + } + op = op.Let(let) + } return coll.findAndModify(ctx, op) } @@ -1482,6 +1527,13 @@ func (coll *Collection) FindOneAndReplace(ctx context.Context, filter interface{ } op = op.Hint(hint) } + if fo.Let != nil { + let, err := transformBsoncoreDocument(coll.registry, fo.Let, true, "let") + if err != nil { + return &SingleResult{err: err} + } + op = op.Let(let) + } return coll.findAndModify(ctx, op) } @@ -1565,6 +1617,13 @@ func (coll *Collection) FindOneAndUpdate(ctx context.Context, filter interface{} } op = op.Hint(hint) } + if fo.Let != nil { + let, err := transformBsoncoreDocument(coll.registry, fo.Let, true, "let") + if err != nil { + return &SingleResult{err: err} + } + op = op.Let(let) + } return coll.findAndModify(ctx, op) } @@ -1680,3 +1739,16 @@ func makeReadPrefSelector(sess *session.Client, selector description.ServerSelec return makePinnedSelector(sess, selector) } + +func makeOutputAggregateSelector(sess *session.Client, rp *readpref.ReadPref, localThreshold time.Duration) description.ServerSelectorFunc { + if sess != nil && sess.TransactionRunning() { + // Use current transaction's read preference if available + rp = sess.CurrentRp + } + + selector := description.CompositeSelector([]description.ServerSelector{ + description.OutputAggregateSelector(rp), + description.LatencySelector(localThreshold), + }) + return makePinnedSelector(sess, selector) +} diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/cursor.go b/vendor/go.mongodb.org/mongo-driver/mongo/cursor.go index 3ec03baf..533cfce0 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/cursor.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/cursor.go @@ -15,6 +15,7 @@ import ( "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/bsoncodec" + "go.mongodb.org/mongo-driver/x/bsonx" "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" "go.mongodb.org/mongo-driver/x/mongo/driver" "go.mongodb.org/mongo-driver/x/mongo/driver/session" @@ -67,6 +68,47 @@ func newEmptyCursor() *Cursor { return &Cursor{bc: driver.NewEmptyBatchCursor()} } +// NewCursorFromDocuments creates a new Cursor pre-loaded with the provided documents, error and registry. If no registry is provided, +// bson.DefaultRegistry will be used. +// +// The documents parameter must be a slice of documents. The slice may be nil or empty, but all elements must be non-nil. +func NewCursorFromDocuments(documents []interface{}, err error, registry *bsoncodec.Registry) (*Cursor, error) { + if registry == nil { + registry = bson.DefaultRegistry + } + + // Convert documents slice to a sequence-style byte array. + var docsBytes []byte + for _, doc := range documents { + switch t := doc.(type) { + case nil: + return nil, ErrNilDocument + case bsonx.Doc: + doc = t.Copy() + case []byte: + // Slight optimization so we'll just use MarshalBSON and not go through the codec machinery. + doc = bson.Raw(t) + } + var marshalErr error + docsBytes, marshalErr = bson.MarshalAppendWithRegistry(registry, docsBytes, doc) + if marshalErr != nil { + return nil, marshalErr + } + } + + c := &Cursor{ + bc: driver.NewBatchCursorFromDocuments(docsBytes), + registry: registry, + err: err, + } + + // Initialize batch and batchLength here. The underlying batch cursor will be preloaded with the + // provided contents, and thus already has a batch before calls to Next/TryNext. + c.batch = c.bc.Batch() + c.batchLength = c.bc.Batch().DocumentCount() + return c, nil +} + // ID returns the ID of this cursor, or 0 if the cursor has been closed or exhausted. func (c *Cursor) ID() int64 { return c.bc.ID() } diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/database.go b/vendor/go.mongodb.org/mongo-driver/mongo/database.go index 20787334..b0066f04 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/database.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/database.go @@ -303,6 +303,9 @@ func (db *Database) Drop(ctx context.Context) error { // documentation). // // For more information about the command, see https://docs.mongodb.com/manual/reference/command/listCollections/. +// +// BUG(benjirewis): ListCollectionSpecifications prevents listing more than 100 collections per database when running +// against MongoDB version 2.6. func (db *Database) ListCollectionSpecifications(ctx context.Context, filter interface{}, opts ...*options.ListCollectionsOptions) ([]*CollectionSpecification, error) { @@ -337,6 +340,9 @@ func (db *Database) ListCollectionSpecifications(ctx context.Context, filter int // documentation). // // For more information about the command, see https://docs.mongodb.com/manual/reference/command/listCollections/. +// +// BUG(benjirewis): ListCollections prevents listing more than 100 collections per database when running against +// MongoDB version 2.6. func (db *Database) ListCollections(ctx context.Context, filter interface{}, opts ...*options.ListCollectionsOptions) (*Cursor, error) { if ctx == nil { ctx = context.Background() @@ -382,6 +388,9 @@ func (db *Database) ListCollections(ctx context.Context, filter interface{}, opt cursorOpts.BatchSize = *lco.BatchSize op = op.BatchSize(*lco.BatchSize) } + if lco.AuthorizedCollections != nil { + op = op.AuthorizedCollections(*lco.AuthorizedCollections) + } retry := driver.RetryNone if db.client.retryReads { @@ -415,6 +424,9 @@ func (db *Database) ListCollections(ctx context.Context, filter interface{}, opt // documentation). // // For more information about the command, see https://docs.mongodb.com/manual/reference/command/listCollections/. +// +// BUG(benjirewis): ListCollectionNames prevents listing more than 100 collections per database when running against +// MongoDB version 2.6. func (db *Database) ListCollectionNames(ctx context.Context, filter interface{}, opts ...*options.ListCollectionsOptions) ([]string, error) { opts = append(opts, options.ListCollections().SetNameOnly(true)) diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/description/server.go b/vendor/go.mongodb.org/mongo-driver/mongo/description/server.go index 7de7df5e..405efe94 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/description/server.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/description/server.go @@ -25,7 +25,7 @@ type SelectedServer struct { Kind TopologyKind } -// Server contains information about a node in a cluster. This is created from isMaster command responses. If the value +// Server contains information about a node in a cluster. This is created from hello command responses. If the value // of the Kind field is LoadBalancer, only the Addr and Kind fields will be set. All other fields will be set to the // zero value of the field's type. type Server struct { @@ -38,6 +38,7 @@ type Server struct { CanonicalAddr address.Address ElectionID primitive.ObjectID HeartbeatInterval time.Duration + HelloOK bool Hosts []string LastError error LastUpdateTime time.Time @@ -47,6 +48,7 @@ type Server struct { MaxMessageSize uint32 Members []address.Address Passives []string + Passive bool Primary address.Address ReadOnly bool ServiceID *primitive.ObjectID // Only set for servers that are deployed behind a load balancer. @@ -59,7 +61,7 @@ type Server struct { WireVersion *VersionRange } -// NewServer creates a new server description from the given isMaster command response. +// NewServer creates a new server description from the given hello command response. func NewServer(addr address.Address, response bson.Raw) Server { desc := Server{Addr: addr, CanonicalAddr: addr, LastUpdateTime: time.Now().UTC()} elements, err := response.Elements() @@ -99,6 +101,12 @@ func NewServer(addr address.Address, response bson.Raw) Server { desc.LastError = fmt.Errorf("expected 'electionId' to be a objectID but it's a BSON %s", element.Value().Type) return desc } + case "helloOk": + desc.HelloOK, ok = element.Value().BooleanOK() + if !ok { + desc.LastError = fmt.Errorf("expected 'helloOk' to be a boolean but it's a BSON %s", element.Value().Type) + return desc + } case "hidden": hidden, ok = element.Value().BooleanOK() if !ok { @@ -118,10 +126,10 @@ func NewServer(addr address.Address, response bson.Raw) Server { desc.LastError = fmt.Errorf("expected 'isWritablePrimary' to be a boolean but it's a BSON %s", element.Value().Type) return desc } - case "ismaster": + case internal.LegacyHelloLowercase: isWritablePrimary, ok = element.Value().BooleanOK() if !ok { - desc.LastError = fmt.Errorf("expected 'ismaster' to be a boolean but it's a BSON %s", element.Value().Type) + desc.LastError = fmt.Errorf("expected legacy hello to be a boolean but it's a BSON %s", element.Value().Type) return desc } case "isreplicaset": @@ -215,6 +223,12 @@ func NewServer(addr address.Address, response bson.Raw) Server { desc.LastError = err return desc } + case "passive": + desc.Passive, ok = element.Value().BooleanOK() + if !ok { + desc.LastError = fmt.Errorf("expected 'passive' to be a boolean but it's a BSON %s", element.Value().Type) + return desc + } case "primary": primary, ok := element.Value().StringValueOK() if !ok { @@ -272,10 +286,6 @@ func NewServer(addr address.Address, response bson.Raw) Server { desc.LastError = err return desc } - - if internal.SetMockServiceID { - desc.ServiceID = &desc.TopologyVersion.ProcessID - } } } diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/description/server_selector.go b/vendor/go.mongodb.org/mongo-driver/mongo/description/server_selector.go index f0c728e4..8e810cb9 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/description/server_selector.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/description/server_selector.go @@ -130,10 +130,20 @@ func WriteSelector() ServerSelector { // ReadPrefSelector selects servers based on the provided read preference. func ReadPrefSelector(rp *readpref.ReadPref) ServerSelector { + return readPrefSelector(rp, false) +} + +// OutputAggregateSelector selects servers based on the provided read preference given that the underlying operation is +// aggregate with an output stage. +func OutputAggregateSelector(rp *readpref.ReadPref) ServerSelector { + return readPrefSelector(rp, true) +} + +func readPrefSelector(rp *readpref.ReadPref, isOutputAggregate bool) ServerSelector { return ServerSelectorFunc(func(t Topology, candidates []Server) ([]Server, error) { if t.Kind == LoadBalanced { // In LoadBalanced mode, there should only be one server in the topology and it must be selected. We check - // this before checking MaxStaleness support becuase there's no monitoring in this mode, so the candidate + // this before checking MaxStaleness support because there's no monitoring in this mode, so the candidate // server wouldn't have a wire version set, which would result in an error. return candidates, nil } @@ -152,7 +162,7 @@ func ReadPrefSelector(rp *readpref.ReadPref) ServerSelector { case Single: return candidates, nil case ReplicaSetNoPrimary, ReplicaSetWithPrimary: - return selectForReplicaSet(rp, t, candidates) + return selectForReplicaSet(rp, isOutputAggregate, t, candidates) case Sharded: return selectByKind(candidates, Mongos), nil } @@ -170,11 +180,21 @@ func maxStalenessSupported(wireVersion *VersionRange) error { return nil } -func selectForReplicaSet(rp *readpref.ReadPref, t Topology, candidates []Server) ([]Server, error) { +func selectForReplicaSet(rp *readpref.ReadPref, isOutputAggregate bool, t Topology, candidates []Server) ([]Server, error) { if err := verifyMaxStaleness(rp, t); err != nil { return nil, err } + // If underlying operation is an aggregate with an output stage, only apply read preference + // if all candidates are 5.0+. Otherwise, operate under primary read preference. + if isOutputAggregate { + for _, s := range candidates { + if s.WireVersion.Max < 13 { + return selectByKind(candidates, RSPrimary), nil + } + } + } + switch rp.Mode() { case readpref.PrimaryMode: return selectByKind(candidates, RSPrimary), nil diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/doc.go b/vendor/go.mongodb.org/mongo-driver/mongo/doc.go index d184a575..669aa14c 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/doc.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/doc.go @@ -105,8 +105,8 @@ // // Note: Auto encryption is an enterprise-only feature. // -// The libmongocrypt C library is required when using client-side encryption. libmongocrypt version 1.1.0 or higher is -// required when using driver version 1.5.0 or higher. To install libmongocrypt, follow the instructions for your +// The libmongocrypt C library is required when using client-side encryption. libmongocrypt version 1.3.0 or higher is +// required when using driver version 1.8.0 or higher. To install libmongocrypt, follow the instructions for your // operating system: // // 1. Linux: follow the instructions listed at diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/errors.go b/vendor/go.mongodb.org/mongo-driver/mongo/errors.go index 2c3ae157..a16efab0 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/errors.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/errors.go @@ -56,6 +56,7 @@ func replaceErrors(err error) error { Labels: de.Labels, Name: de.Name, Wrapped: de.Wrapped, + Raw: bson.Raw(de.Raw), } } if qe, ok := err.(driver.QueryFailureError); ok { @@ -63,6 +64,7 @@ func replaceErrors(err error) error { ce := CommandError{ Name: qe.Message, Wrapped: qe.Wrapped, + Raw: bson.Raw(qe.Response), } dollarErr, err := qe.Response.LookupErr("$err") @@ -207,6 +209,7 @@ type ServerError interface { } var _ ServerError = CommandError{} +var _ ServerError = WriteError{} var _ ServerError = WriteException{} var _ ServerError = BulkWriteException{} @@ -217,6 +220,7 @@ type CommandError struct { Labels []string // Categories to which the error belongs Name string // A human-readable name corresponding to the error code Wrapped error // The underlying error, if one exists. + Raw bson.Raw // The original server response containing the error. } // Error implements the error interface. @@ -276,6 +280,9 @@ type WriteError struct { Code int Message string Details bson.Raw + + // The original write error from the server response. + Raw bson.Raw } func (we WriteError) Error() string { @@ -286,6 +293,30 @@ func (we WriteError) Error() string { return msg } +// HasErrorCode returns true if the error has the specified code. +func (we WriteError) HasErrorCode(code int) bool { + return we.Code == code +} + +// HasErrorLabel returns true if the error contains the specified label. WriteErrors do not contain labels, +// so we always return false. +func (we WriteError) HasErrorLabel(label string) bool { + return false +} + +// HasErrorMessage returns true if the error contains the specified message. +func (we WriteError) HasErrorMessage(message string) bool { + return strings.Contains(we.Message, message) +} + +// HasErrorCodeWithMessage returns true if the error has the specified code and Message contains the specified message. +func (we WriteError) HasErrorCodeWithMessage(code int, message string) bool { + return we.Code == code && strings.Contains(we.Message, message) +} + +// serverError implements the ServerError interface. +func (we WriteError) serverError() {} + // WriteErrors is a group of write errors that occurred during execution of a write operation. type WriteErrors []WriteError @@ -307,6 +338,7 @@ func writeErrorsFromDriverWriteErrors(errs driver.WriteErrors) WriteErrors { Code: int(err.Code), Message: err.Message, Details: bson.Raw(err.Details), + Raw: bson.Raw(err.Raw), }) } return wes @@ -319,6 +351,7 @@ type WriteConcernError struct { Code int Message string Details bson.Raw + Raw bson.Raw // The original write concern error from the server response. } // Error implements the error interface. @@ -340,6 +373,9 @@ type WriteException struct { // The categories to which the exception belongs. Labels []string + + // The original server response containing the error. + Raw bson.Raw } // Error implements the error interface. @@ -426,6 +462,7 @@ func convertDriverWriteConcernError(wce *driver.WriteConcernError) *WriteConcern Code: int(wce.Code), Message: wce.Message, Details: bson.Raw(wce.Details), + Raw: bson.Raw(wce.Raw), } } @@ -559,6 +596,7 @@ func processWriteError(err error) (returnResult, error) { WriteConcernError: convertDriverWriteConcernError(tt.WriteConcernError), WriteErrors: writeErrorsFromDriverWriteErrors(tt.WriteErrors), Labels: tt.Labels, + Raw: bson.Raw(tt.Raw), } default: return rrNone, replaceErrors(err) diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/index_view.go b/vendor/go.mongodb.org/mongo-driver/mongo/index_view.go index 18fc5244..e8e260f1 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/index_view.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/index_view.go @@ -272,7 +272,8 @@ func (iv IndexView) CreateMany(ctx context.Context, models []IndexModel, opts .. err = op.Execute(ctx) if err != nil { - return nil, replaceErrors(err) + _, err = processWriteError(err) + return nil, err } return names, nil diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/mongo.go b/vendor/go.mongodb.org/mongo-driver/mongo/mongo.go index 3ceb7aa9..da29175c 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/mongo.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/mongo.go @@ -161,21 +161,6 @@ func transformBsoncoreDocument(registry *bsoncodec.Registry, val interface{}, ma return b, nil } -func ensureID(d bsonx.Doc) (bsonx.Doc, interface{}) { - var id interface{} - - elem, err := d.LookupElementErr("_id") - switch err.(type) { - case nil: - id = elem - default: - oid := primitive.NewObjectID() - d = append(d, bsonx.Elem{"_id", bsonx.ObjectID(oid)}) - id = oid - } - return d, id -} - func ensureDollarKey(doc bsoncore.Document) error { firstElem, err := doc.IndexErr(0) if err != nil { @@ -190,7 +175,7 @@ func ensureDollarKey(doc bsoncore.Document) error { func ensureNoDollarKey(doc bsoncore.Document) error { if elem, err := doc.IndexErr(0); err == nil && strings.HasPrefix(elem.Key(), "$") { - return errors.New("replacement document cannot contains keys beginning with '$") + return errors.New("replacement document cannot contain keys beginning with '$'") } return nil @@ -225,20 +210,43 @@ func transformAggregatePipeline(registry *bsoncodec.Registry, pipeline interface return nil, false, fmt.Errorf("can only transform slices and arrays into aggregation pipelines, but got %v", val.Kind()) } - aidx, arr := bsoncore.AppendArrayStart(nil) var hasOutputStage bool valLen := val.Len() + switch t := pipeline.(type) { // Explicitly forbid non-empty pipelines that are semantically single documents // and are implemented as slices. - switch t := pipeline.(type) { case bson.D, bson.Raw, bsoncore.Document: if valLen > 0 { return nil, false, fmt.Errorf("%T is not an allowed pipeline type as it represents a single document. Use bson.A or mongo.Pipeline instead", t) } + // bsoncore.Arrays do not need to be transformed. Only check validity and presence of output stage. + case bsoncore.Array: + if err := t.Validate(); err != nil { + return nil, false, err + } + + values, err := t.Values() + if err != nil { + return nil, false, err + } + + numVals := len(values) + if numVals == 0 { + return bsoncore.Document(t), false, nil + } + + // If not empty, check if first value of the last stage is $out or $merge. + if lastStage, ok := values[numVals-1].DocumentOK(); ok { + if elem, err := lastStage.IndexErr(0); err == nil && (elem.Key() == "$out" || elem.Key() == "$merge") { + hasOutputStage = true + } + } + return bsoncore.Document(t), hasOutputStage, nil } + aidx, arr := bsoncore.AppendArrayStart(nil) for idx := 0; idx < valLen; idx++ { doc, err := transformBsoncoreDocument(registry, val.Index(idx).Interface(), true, fmt.Sprintf("pipeline stage :%v", idx)) if err != nil { diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/mongocryptd.go b/vendor/go.mongodb.org/mongo-driver/mongo/mongocryptd.go index dac99746..c36b1d31 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/mongocryptd.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/mongocryptd.go @@ -115,6 +115,8 @@ func (mc *mcryptClient) disconnect(ctx context.Context) error { } func (mc *mcryptClient) spawnProcess() error { + // Ignore gosec warning about subprocess launched with externally-provided path variable. + /* #nosec G204 */ cmd := exec.Command(mc.path, mc.spawnArgs...) cmd.Stdout = nil cmd.Stderr = nil diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/aggregateoptions.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/aggregateoptions.go index e1f710fd..cf0da5fc 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/aggregateoptions.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/options/aggregateoptions.go @@ -6,7 +6,11 @@ package options -import "time" +import ( + "time" + + "go.mongodb.org/mongo-driver/bson" +) // AggregateOptions represents options that can be used to configure an Aggregate operation. type AggregateOptions struct { @@ -50,6 +54,11 @@ type AggregateOptions struct { // Values must be constant or closed expressions that do not reference document fields. Parameters can then be // accessed as variables in an aggregate expression context (e.g. "$$var"). Let interface{} + + // Custom options to be added to aggregate expression. Key-value pairs of the BSON map should correlate with desired + // option names and values. Values must be Marshalable. Custom options may conflict with non-custom options, and custom + // options bypass client-side validation. Prefer using non-custom options where possible. + Custom bson.M } // Aggregate creates a new AggregateOptions instance. @@ -111,6 +120,15 @@ func (ao *AggregateOptions) SetLet(let interface{}) *AggregateOptions { return ao } +// SetCustom sets the value for the Custom field. Key-value pairs of the BSON map should correlate +// with desired option names and values. Values must be Marshalable. Custom options may conflict +// with non-custom options, and custom options bypass client-side validation. Prefer using non-custom +// options where possible. +func (ao *AggregateOptions) SetCustom(c bson.M) *AggregateOptions { + ao.Custom = c + return ao +} + // MergeAggregateOptions combines the given AggregateOptions instances into a single AggregateOptions in a last-one-wins // fashion. func MergeAggregateOptions(opts ...*AggregateOptions) *AggregateOptions { @@ -146,6 +164,9 @@ func MergeAggregateOptions(opts ...*AggregateOptions) *AggregateOptions { if ao.Let != nil { aggOpts.Let = ao.Let } + if ao.Custom != nil { + aggOpts.Custom = ao.Custom + } } return aggOpts diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/autoencryptionoptions.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/autoencryptionoptions.go index 517b69e6..89c3c05f 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/autoencryptionoptions.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/options/autoencryptionoptions.go @@ -6,6 +6,10 @@ package options +import ( + "crypto/tls" +) + // AutoEncryptionOptions represents options used to configure auto encryption/decryption behavior for a mongo.Client // instance. // @@ -27,6 +31,7 @@ type AutoEncryptionOptions struct { SchemaMap map[string]interface{} BypassAutoEncryption *bool ExtraOptions map[string]interface{} + TLSConfig map[string]*tls.Config } // AutoEncryption creates a new AutoEncryptionOptions configured with default values. @@ -91,6 +96,23 @@ func (a *AutoEncryptionOptions) SetExtraOptions(extraOpts map[string]interface{} return a } +// SetTLSConfig specifies tls.Config instances for each KMS provider to use to configure TLS on all connections created +// to the KMS provider. +// +// This should only be used to set custom TLS configurations. By default, the connection will use an empty tls.Config{} with MinVersion set to tls.VersionTLS12. +func (a *AutoEncryptionOptions) SetTLSConfig(tlsOpts map[string]*tls.Config) *AutoEncryptionOptions { + tlsConfigs := make(map[string]*tls.Config) + for provider, config := range tlsOpts { + // use TLS min version 1.2 to enforce more secure hash algorithms and advanced cipher suites + if config.MinVersion == 0 { + config.MinVersion = tls.VersionTLS12 + } + tlsConfigs[provider] = config + } + a.TLSConfig = tlsConfigs + return a +} + // MergeAutoEncryptionOptions combines the argued AutoEncryptionOptions in a last-one wins fashion. func MergeAutoEncryptionOptions(opts ...*AutoEncryptionOptions) *AutoEncryptionOptions { aeo := AutoEncryption() @@ -117,6 +139,9 @@ func MergeAutoEncryptionOptions(opts ...*AutoEncryptionOptions) *AutoEncryptionO if opt.ExtraOptions != nil { aeo.ExtraOptions = opt.ExtraOptions } + if opt.TLSConfig != nil { + aeo.TLSConfig = opt.TLSConfig + } } return aeo diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/bulkwriteoptions.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/bulkwriteoptions.go index 57f98f83..2786ab2c 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/bulkwriteoptions.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/options/bulkwriteoptions.go @@ -19,6 +19,12 @@ type BulkWriteOptions struct { // If true, no writes will be executed after one fails. The default value is true. Ordered *bool + + // Specifies parameters for all update and delete commands in the BulkWrite. This option is only valid for MongoDB + // versions >= 5.0. Older servers will report an error for using this option. This must be a document mapping + // parameter names to values. Values must be constant or closed expressions that do not reference document fields. + // Parameters can then be accessed as variables in an aggregate expression context (e.g. "$$var"). + Let interface{} } // BulkWrite creates a new *BulkWriteOptions instance. @@ -40,6 +46,15 @@ func (b *BulkWriteOptions) SetBypassDocumentValidation(bypass bool) *BulkWriteOp return b } +// SetLet sets the value for the Let field. Let specifies parameters for all update and delete commands in the BulkWrite. +// This option is only valid for MongoDB versions >= 5.0. Older servers will report an error for using this option. +// This must be a document mapping parameter names to values. Values must be constant or closed expressions that do not +// reference document fields. Parameters can then be accessed as variables in an aggregate expression context (e.g. "$$var"). +func (b *BulkWriteOptions) SetLet(let interface{}) *BulkWriteOptions { + b.Let = &let + return b +} + // MergeBulkWriteOptions combines the given BulkWriteOptions instances into a single BulkWriteOptions in a last-one-wins // fashion. func MergeBulkWriteOptions(opts ...*BulkWriteOptions) *BulkWriteOptions { @@ -54,6 +69,9 @@ func MergeBulkWriteOptions(opts ...*BulkWriteOptions) *BulkWriteOptions { if opt.BypassDocumentValidation != nil { b.BypassDocumentValidation = opt.BypassDocumentValidation } + if opt.Let != nil { + b.Let = opt.Let + } } return b diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/changestreamoptions.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/changestreamoptions.go index fe19f45e..eb9b0643 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/changestreamoptions.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/options/changestreamoptions.go @@ -9,6 +9,7 @@ package options import ( "time" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -46,6 +47,16 @@ type ChangeStreamOptions struct { // corresponding to an oplog entry immediately after the specified token will be returned. If this is specified, // ResumeAfter and StartAtOperationTime must not be set. This option is only valid for MongoDB versions >= 4.1.1. StartAfter interface{} + + // Custom options to be added to the initial aggregate for the change stream. Key-value pairs of the BSON map should + // correlate with desired option names and values. Values must be Marshalable. Custom options may conflict with + // non-custom options, and custom options bypass client-side validation. Prefer using non-custom options where possible. + Custom bson.M + + // Custom options to be added to the $changeStream stage in the initial aggregate. Key-value pairs of the BSON map should + // correlate with desired option names and values. Values must be Marshalable. Custom pipeline options bypass client-side + // validation. Prefer using non-custom options where possible. + CustomPipeline bson.M } // ChangeStream creates a new ChangeStreamOptions instance. @@ -97,6 +108,23 @@ func (cso *ChangeStreamOptions) SetStartAfter(sa interface{}) *ChangeStreamOptio return cso } +// SetCustom sets the value for the Custom field. Key-value pairs of the BSON map should correlate +// with desired option names and values. Values must be Marshalable. Custom options may conflict +// with non-custom options, and custom options bypass client-side validation. Prefer using non-custom +// options where possible. +func (cso *ChangeStreamOptions) SetCustom(c bson.M) *ChangeStreamOptions { + cso.Custom = c + return cso +} + +// SetCustomPipeline sets the value for the CustomPipeline field. Key-value pairs of the BSON map +// should correlate with desired option names and values. Values must be Marshalable. Custom pipeline +// options bypass client-side validation. Prefer using non-custom options where possible. +func (cso *ChangeStreamOptions) SetCustomPipeline(cp bson.M) *ChangeStreamOptions { + cso.CustomPipeline = cp + return cso +} + // MergeChangeStreamOptions combines the given ChangeStreamOptions instances into a single ChangeStreamOptions in a // last-one-wins fashion. func MergeChangeStreamOptions(opts ...*ChangeStreamOptions) *ChangeStreamOptions { @@ -126,6 +154,12 @@ func MergeChangeStreamOptions(opts ...*ChangeStreamOptions) *ChangeStreamOptions if cso.StartAfter != nil { csOpts.StartAfter = cso.StartAfter } + if cso.Custom != nil { + csOpts.Custom = cso.Custom + } + if cso.CustomPipeline != nil { + csOpts.CustomPipeline = cso.CustomPipeline + } } return csOpts diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/clientencryptionoptions.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/clientencryptionoptions.go index ea770413..b8f6e871 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/clientencryptionoptions.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/options/clientencryptionoptions.go @@ -6,10 +6,16 @@ package options +import ( + "crypto/tls" + "fmt" +) + // ClientEncryptionOptions represents all possible options used to configure a ClientEncryption instance. type ClientEncryptionOptions struct { KeyVaultNamespace string KmsProviders map[string]map[string]interface{} + TLSConfig map[string]*tls.Config } // ClientEncryption creates a new ClientEncryptionOptions instance. @@ -29,6 +35,86 @@ func (c *ClientEncryptionOptions) SetKmsProviders(providers map[string]map[strin return c } +// SetTLSConfig specifies tls.Config instances for each KMS provider to use to configure TLS on all connections created +// to the KMS provider. +// +// This should only be used to set custom TLS configurations. By default, the connection will use an empty tls.Config{} with MinVersion set to tls.VersionTLS12. +func (c *ClientEncryptionOptions) SetTLSConfig(tlsOpts map[string]*tls.Config) *ClientEncryptionOptions { + tlsConfigs := make(map[string]*tls.Config) + for provider, config := range tlsOpts { + // use TLS min version 1.2 to enforce more secure hash algorithms and advanced cipher suites + if config.MinVersion == 0 { + config.MinVersion = tls.VersionTLS12 + } + tlsConfigs[provider] = config + } + c.TLSConfig = tlsConfigs + return c +} + +// BuildTLSConfig specifies tls.Config options for each KMS provider to use to configure TLS on all connections created +// to the KMS provider. The input map should contain a mapping from each KMS provider to a document containing the necessary +// options, as follows: +// +// { +// "kmip": { +// "tlsCertificateKeyFile": "foo.pem", +// "tlsCAFile": "fooCA.pem" +// } +// } +// +// Currently, the following TLS options are supported: +// +// 1. "tlsCertificateKeyFile" (or "sslClientCertificateKeyFile"): The "tlsCertificateKeyFile" option specifies a path to +// the client certificate and private key, which must be concatenated into one file. +// +// 2. "tlsCertificateKeyFilePassword" (or "sslClientCertificateKeyPassword"): Specify the password to decrypt the client +// private key file (e.g. "tlsCertificateKeyFilePassword=password"). +// +// 3. "tlsCaFile" (or "sslCertificateAuthorityFile"): Specify the path to a single or bundle of certificate authorities +// to be considered trusted when making a TLS connection (e.g. "tlsCaFile=/path/to/caFile"). +// +// This should only be used to set custom TLS options. By default, the connection will use an empty tls.Config{} with MinVersion set to tls.VersionTLS12. +func BuildTLSConfig(tlsOpts map[string]interface{}) (*tls.Config, error) { + // use TLS min version 1.2 to enforce more secure hash algorithms and advanced cipher suites + cfg := &tls.Config{MinVersion: tls.VersionTLS12} + + for name := range tlsOpts { + var err error + switch name { + case "tlsCertificateKeyFile", "sslClientCertificateKeyFile": + clientCertPath, ok := tlsOpts[name].(string) + if !ok { + return nil, fmt.Errorf("expected %q value to be of type string, got %T", name, tlsOpts[name]) + } + // apply custom key file password if found, otherwise use empty string + if keyPwd, found := tlsOpts["tlsCertificateKeyFilePassword"].(string); found { + _, err = addClientCertFromConcatenatedFile(cfg, clientCertPath, keyPwd) + } else if keyPwd, found := tlsOpts["sslClientCertificateKeyPassword"].(string); found { + _, err = addClientCertFromConcatenatedFile(cfg, clientCertPath, keyPwd) + } else { + _, err = addClientCertFromConcatenatedFile(cfg, clientCertPath, "") + } + case "tlsCertificateKeyFilePassword", "sslClientCertificateKeyPassword": + continue + case "tlsCAFile", "sslCertificateAuthorityFile": + caPath, ok := tlsOpts[name].(string) + if !ok { + return nil, fmt.Errorf("expected %q value to be of type string, got %T", name, tlsOpts[name]) + } + err = addCACertFromFile(cfg, caPath) + default: + return nil, fmt.Errorf("unrecognized TLS option %v", name) + } + + if err != nil { + return nil, err + } + } + + return cfg, nil +} + // MergeClientEncryptionOptions combines the argued ClientEncryptionOptions in a last-one wins fashion. func MergeClientEncryptionOptions(opts ...*ClientEncryptionOptions) *ClientEncryptionOptions { ceo := ClientEncryption() @@ -43,6 +129,9 @@ func MergeClientEncryptionOptions(opts ...*ClientEncryptionOptions) *ClientEncry if opt.KmsProviders != nil { ceo.KmsProviders = opt.KmsProviders } + if opt.TLSConfig != nil { + ceo.TLSConfig = opt.TLSConfig + } } return ceo diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/clientoptions.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/clientoptions.go index d66114fa..115cc642 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/clientoptions.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/options/clientoptions.go @@ -109,6 +109,7 @@ type ClientOptions struct { MaxConnIdleTime *time.Duration MaxPoolSize *uint64 MinPoolSize *uint64 + MaxConnecting *uint64 PoolMonitor *event.PoolMonitor Monitor *event.CommandMonitor ServerMonitor *event.ServerMonitor @@ -121,6 +122,8 @@ type ClientOptions struct { ServerAPIOptions *ServerAPIOptions ServerSelectionTimeout *time.Duration SocketTimeout *time.Duration + SRVMaxHosts *int + SRVServiceName *string TLSConfig *tls.Config WriteConcern *writeconcern.WriteConcern ZlibLevel *int @@ -136,6 +139,13 @@ type ClientOptions struct { // release. AuthenticateToAnything *bool + // Crypt specifies a custom driver.Crypt to be used to encrypt and decrypt documents. The default is no + // encryption. + // + // Deprecated: This option is for internal use only and should not be set (see GODRIVER-2149). It may be + // changed or removed in any release. + Crypt driver.Crypt + // Deployment specifies a custom deployment to use for the new Client. // // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any @@ -180,12 +190,25 @@ func (c *ClientOptions) validateAndSetError() { if c.LoadBalanced != nil && *c.LoadBalanced { if len(c.Hosts) > 1 { c.err = internal.ErrLoadBalancedWithMultipleHosts + return } if c.ReplicaSet != nil { c.err = internal.ErrLoadBalancedWithReplicaSet + return } if c.Direct != nil { c.err = internal.ErrLoadBalancedWithDirectConnection + return + } + } + + // Validation for srvMaxHosts. + if c.SRVMaxHosts != nil && *c.SRVMaxHosts > 0 { + if c.ReplicaSet != nil { + c.err = internal.ErrSRVMaxHostsWithReplicaSet + } + if c.LoadBalanced != nil && *c.LoadBalanced { + c.err = internal.ErrSRVMaxHostsWithLoadBalanced } } } @@ -290,6 +313,10 @@ func (c *ClientOptions) ApplyURI(uri string) *ClientOptions { c.MinPoolSize = &cs.MinPoolSize } + if cs.MaxConnectingSet { + c.MaxConnecting = &cs.MaxConnecting + } + if cs.ReadConcernLevel != "" { c.ReadConcern = readconcern.New(readconcern.Level(cs.ReadConcernLevel)) } @@ -338,6 +365,14 @@ func (c *ClientOptions) ApplyURI(uri string) *ClientOptions { c.SocketTimeout = &cs.SocketTimeout } + if cs.SRVMaxHosts != 0 { + c.SRVMaxHosts = &cs.SRVMaxHosts + } + + if cs.SRVServiceName != "" { + c.SRVServiceName = &cs.SRVServiceName + } + if cs.SSL { tlsConfig := new(tls.Config) @@ -539,7 +574,7 @@ func (c *ClientOptions) SetMaxConnIdleTime(d time.Duration) *ClientOptions { // SetMaxPoolSize specifies that maximum number of connections allowed in the driver's connection pool to each server. // Requests to a server will block if this maximum is reached. This can also be set through the "maxPoolSize" URI option -// (e.g. "maxPoolSize=100"). The default is 100. If this is 0, it will be set to math.MaxInt64. +// (e.g. "maxPoolSize=100"). If this is 0, maximum connection pool size is not limited. The default is 100. func (c *ClientOptions) SetMaxPoolSize(u uint64) *ClientOptions { c.MaxPoolSize = &u return c @@ -553,6 +588,14 @@ func (c *ClientOptions) SetMinPoolSize(u uint64) *ClientOptions { return c } +// SetMaxConnecting specifies the maximum number of connections a connection pool may establish simultaneously. This can +// also be set through the "maxConnecting" URI option (e.g. "maxConnecting=2"). If this is 0, the default is used. The +// default is 2. Values greater than 100 are not recommended. +func (c *ClientOptions) SetMaxConnecting(u uint64) *ClientOptions { + c.MaxConnecting = &u + return c +} + // SetPoolMonitor specifies a PoolMonitor to receive connection pool events. See the event.PoolMonitor documentation // for more information about the structure of the monitor and events that can be received. func (c *ClientOptions) SetPoolMonitor(m *event.PoolMonitor) *ClientOptions { @@ -760,6 +803,22 @@ func (c *ClientOptions) SetServerAPIOptions(opts *ServerAPIOptions) *ClientOptio return c } +// SetSRVMaxHosts specifies the maximum number of SRV results to randomly select during polling. To limit the number +// of hosts selected in SRV discovery, this function must be called before ApplyURI. This can also be set through +// the "srvMaxHosts" URI option. +func (c *ClientOptions) SetSRVMaxHosts(srvMaxHosts int) *ClientOptions { + c.SRVMaxHosts = &srvMaxHosts + return c +} + +// SetSRVServiceName specifies a custom SRV service name to use in SRV polling. To use a custom SRV service name +// in SRV discovery, this function must be called before ApplyURI. This can also be set through the "srvServiceName" +// URI option. +func (c *ClientOptions) SetSRVServiceName(srvName string) *ClientOptions { + c.SRVServiceName = &srvName + return c +} + // MergeClientOptions combines the given *ClientOptions into a single *ClientOptions in a last one wins fashion. // The specified options are merged with the existing options on the client, with the specified options taking // precedence. @@ -789,6 +848,9 @@ func MergeClientOptions(opts ...*ClientOptions) *ClientOptions { if opt.ConnectTimeout != nil { c.ConnectTimeout = opt.ConnectTimeout } + if opt.Crypt != nil { + c.Crypt = opt.Crypt + } if opt.HeartbeatInterval != nil { c.HeartbeatInterval = opt.HeartbeatInterval } @@ -810,6 +872,9 @@ func MergeClientOptions(opts ...*ClientOptions) *ClientOptions { if opt.MinPoolSize != nil { c.MinPoolSize = opt.MinPoolSize } + if opt.MaxConnecting != nil { + c.MaxConnecting = opt.MaxConnecting + } if opt.PoolMonitor != nil { c.PoolMonitor = opt.PoolMonitor } @@ -849,6 +914,12 @@ func MergeClientOptions(opts ...*ClientOptions) *ClientOptions { if opt.SocketTimeout != nil { c.SocketTimeout = opt.SocketTimeout } + if opt.SRVMaxHosts != nil { + c.SRVMaxHosts = opt.SRVMaxHosts + } + if opt.SRVServiceName != nil { + c.SRVServiceName = opt.SRVServiceName + } if opt.TLSConfig != nil { c.TLSConfig = opt.TLSConfig } @@ -930,7 +1001,8 @@ func addClientCertFromConcatenatedFile(cfg *tls.Config, certKeyFile, keyPassword // containing file and returns the certificate's subject name. func addClientCertFromBytes(cfg *tls.Config, data []byte, keyPasswd string) (string, error) { var currentBlock *pem.Block - var certBlock, certDecodedBlock, keyBlock []byte + var certDecodedBlock []byte + var certBlocks, keyBlocks [][]byte remaining := data start := 0 @@ -941,7 +1013,8 @@ func addClientCertFromBytes(cfg *tls.Config, data []byte, keyPasswd string) (str } if currentBlock.Type == "CERTIFICATE" { - certBlock = data[start : len(data)-len(remaining)] + certBlock := data[start : len(data)-len(remaining)] + certBlocks = append(certBlocks, certBlock) certDecodedBlock = currentBlock.Bytes start += len(certBlock) } else if strings.HasSuffix(currentBlock.Type, "PRIVATE KEY") { @@ -966,29 +1039,31 @@ func addClientCertFromBytes(cfg *tls.Config, data []byte, keyPasswd string) (str if err != nil { return "", err } - keyBytes, err = x509MarshalPKCS8PrivateKey(decrypted) + keyBytes, err = x509.MarshalPKCS8PrivateKey(decrypted) if err != nil { return "", err } } var encoded bytes.Buffer pem.Encode(&encoded, &pem.Block{Type: currentBlock.Type, Bytes: keyBytes}) - keyBlock = encoded.Bytes() + keyBlock := encoded.Bytes() + keyBlocks = append(keyBlocks, keyBlock) start = len(data) - len(remaining) } else { - keyBlock = data[start : len(data)-len(remaining)] + keyBlock := data[start : len(data)-len(remaining)] + keyBlocks = append(keyBlocks, keyBlock) start += len(keyBlock) } } } - if len(certBlock) == 0 { + if len(certBlocks) == 0 { return "", fmt.Errorf("failed to find CERTIFICATE") } - if len(keyBlock) == 0 { + if len(keyBlocks) == 0 { return "", fmt.Errorf("failed to find PRIVATE KEY") } - cert, err := tls.X509KeyPair(certBlock, keyBlock) + cert, err := tls.X509KeyPair(bytes.Join(certBlocks, []byte("\n")), bytes.Join(keyBlocks, []byte("\n"))) if err != nil { return "", err } @@ -1002,7 +1077,7 @@ func addClientCertFromBytes(cfg *tls.Config, data []byte, keyPasswd string) (str return "", err } - return x509CertSubject(crt), nil + return crt.Subject.String(), nil } func stringSliceContains(source []string, target string) bool { diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/clientoptions_1_10.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/clientoptions_1_10.go deleted file mode 100644 index 1943d9c5..00000000 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/clientoptions_1_10.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build go1.10 - -package options - -import "crypto/x509" - -func x509CertSubject(cert *x509.Certificate) string { - return cert.Subject.String() -} - -func x509MarshalPKCS8PrivateKey(pkcs8 interface{}) ([]byte, error) { - return x509.MarshalPKCS8PrivateKey(pkcs8) -} diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/clientoptions_1_9.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/clientoptions_1_9.go deleted file mode 100644 index e1099229..00000000 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/clientoptions_1_9.go +++ /dev/null @@ -1,20 +0,0 @@ -// +build !go1.10 - -package options - -import ( - "crypto/x509" - "fmt" -) - -// We don't support Go versions less than 1.10, but Evergreen needs to be able to compile the driver -// using version 1.9 and cert.Subject -func x509CertSubject(cert *x509.Certificate) string { - return "" -} - -// We don't support Go versions less than 1.10, but Evergreen needs to be able to compile the driver -// using version 1.9 and x509.MarshalPKCS8PrivateKey() -func x509MarshalPKCS8PrivateKey(pkcs8 interface{}) ([]byte, error) { - return nil, fmt.Errorf("PKCS8-encrypted client private keys are only supported with go1.10+") -} diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/createcollectionoptions.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/createcollectionoptions.go index 3fe63c32..130c8e75 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/createcollectionoptions.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/options/createcollectionoptions.go @@ -110,10 +110,20 @@ type CreateCollectionOptions struct { // collection. Validator interface{} - // Value indicating after how many seconds old time-series data should be deleted. + // Value indicating after how many seconds old time-series data should be deleted. See + // https://docs.mongodb.com/manual/reference/command/create/ for supported options, and + // https://docs.mongodb.com/manual/core/timeseries-collections/ for more information on time-series + // collections. + // + // This option is only valid for MongoDB versions >= 5.0 ExpireAfterSeconds *int64 - // Options for specifying a time-series collection. + // Options for specifying a time-series collection. See + // https://docs.mongodb.com/manual/reference/command/create/ for supported options, and + // https://docs.mongodb.com/manual/core/timeseries-collections/ for more information on time-series + // collections. + // + // This option is only valid for MongoDB versions >= 5.0 TimeSeriesOptions *TimeSeriesOptions } diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/deleteoptions.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/deleteoptions.go index fcea40a5..0473d81f 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/deleteoptions.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/options/deleteoptions.go @@ -20,6 +20,12 @@ type DeleteOptions struct { // operation. The driver will return an error if the hint parameter is a multi-key map. The default value is nil, // which means that no hint will be sent. Hint interface{} + + // Specifies parameters for the delete expression. This option is only valid for MongoDB versions >= 5.0. Older + // servers will report an error for using this option. This must be a document mapping parameter names to values. + // Values must be constant or closed expressions that do not reference document fields. Parameters can then be + // accessed as variables in an aggregate expression context (e.g. "$$var"). + Let interface{} } // Delete creates a new DeleteOptions instance. @@ -39,6 +45,12 @@ func (do *DeleteOptions) SetHint(hint interface{}) *DeleteOptions { return do } +// SetLet sets the value for the Let field. +func (do *DeleteOptions) SetLet(let interface{}) *DeleteOptions { + do.Let = let + return do +} + // MergeDeleteOptions combines the given DeleteOptions instances into a single DeleteOptions in a last-one-wins fashion. func MergeDeleteOptions(opts ...*DeleteOptions) *DeleteOptions { dOpts := Delete() @@ -52,6 +64,9 @@ func MergeDeleteOptions(opts ...*DeleteOptions) *DeleteOptions { if do.Hint != nil { dOpts.Hint = do.Hint } + if do.Let != nil { + dOpts.Let = do.Let + } } return dOpts diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/findoptions.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/findoptions.go index ad9409c9..0dd09f51 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/findoptions.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/options/findoptions.go @@ -99,6 +99,12 @@ type FindOptions struct { // A document specifying the order in which documents should be returned. The driver will return an error if the // sort parameter is a multi-key map. Sort interface{} + + // Specifies parameters for the find expression. This option is only valid for MongoDB versions >= 5.0. Older + // servers will report an error for using this option. This must be a document mapping parameter names to values. + // Values must be constant or closed expressions that do not reference document fields. Parameters can then be + // accessed as variables in an aggregate expression context (e.g. "$$var"). + Let interface{} } // Find creates a new FindOptions instance. @@ -148,6 +154,12 @@ func (f *FindOptions) SetHint(hint interface{}) *FindOptions { return f } +// SetLet sets the value for the Let field. +func (f *FindOptions) SetLet(let interface{}) *FindOptions { + f.Let = let + return f +} + // SetLimit sets the value for the Limit field. func (f *FindOptions) SetLimit(i int64) *FindOptions { f.Limit = &i @@ -258,6 +270,9 @@ func MergeFindOptions(opts ...*FindOptions) *FindOptions { if opt.Hint != nil { fo.Hint = opt.Hint } + if opt.Let != nil { + fo.Let = opt.Let + } if opt.Limit != nil { fo.Limit = opt.Limit } @@ -624,6 +639,12 @@ type FindOneAndReplaceOptions struct { // will return an error if the hint parameter is a multi-key map. The default value is nil, which means that no hint // will be sent. Hint interface{} + + // Specifies parameters for the find one and replace expression. This option is only valid for MongoDB versions >= 5.0. Older + // servers will report an error for using this option. This must be a document mapping parameter names to values. + // Values must be constant or closed expressions that do not reference document fields. Parameters can then be + // accessed as variables in an aggregate expression context (e.g. "$$var"). + Let interface{} } // FindOneAndReplace creates a new FindOneAndReplaceOptions instance. @@ -679,6 +700,12 @@ func (f *FindOneAndReplaceOptions) SetHint(hint interface{}) *FindOneAndReplaceO return f } +// SetLet sets the value for the Let field. +func (f *FindOneAndReplaceOptions) SetLet(let interface{}) *FindOneAndReplaceOptions { + f.Let = let + return f +} + // MergeFindOneAndReplaceOptions combines the given FindOneAndReplaceOptions instances into a single // FindOneAndReplaceOptions in a last-one-wins fashion. func MergeFindOneAndReplaceOptions(opts ...*FindOneAndReplaceOptions) *FindOneAndReplaceOptions { @@ -711,6 +738,9 @@ func MergeFindOneAndReplaceOptions(opts ...*FindOneAndReplaceOptions) *FindOneAn if opt.Hint != nil { fo.Hint = opt.Hint } + if opt.Let != nil { + fo.Let = opt.Let + } } return fo @@ -762,6 +792,12 @@ type FindOneAndUpdateOptions struct { // will return an error if the hint parameter is a multi-key map. The default value is nil, which means that no hint // will be sent. Hint interface{} + + // Specifies parameters for the find one and update expression. This option is only valid for MongoDB versions >= 5.0. Older + // servers will report an error for using this option. This must be a document mapping parameter names to values. + // Values must be constant or closed expressions that do not reference document fields. Parameters can then be + // accessed as variables in an aggregate expression context (e.g. "$$var"). + Let interface{} } // FindOneAndUpdate creates a new FindOneAndUpdateOptions instance. @@ -823,6 +859,12 @@ func (f *FindOneAndUpdateOptions) SetHint(hint interface{}) *FindOneAndUpdateOpt return f } +// SetLet sets the value for the Let field. +func (f *FindOneAndUpdateOptions) SetLet(let interface{}) *FindOneAndUpdateOptions { + f.Let = let + return f +} + // MergeFindOneAndUpdateOptions combines the given FindOneAndUpdateOptions instances into a single // FindOneAndUpdateOptions in a last-one-wins fashion. func MergeFindOneAndUpdateOptions(opts ...*FindOneAndUpdateOptions) *FindOneAndUpdateOptions { @@ -858,6 +900,9 @@ func MergeFindOneAndUpdateOptions(opts ...*FindOneAndUpdateOptions) *FindOneAndU if opt.Hint != nil { fo.Hint = opt.Hint } + if opt.Let != nil { + fo.Let = opt.Let + } } return fo @@ -890,6 +935,12 @@ type FindOneAndDeleteOptions struct { // will return an error if the hint parameter is a multi-key map. The default value is nil, which means that no hint // will be sent. Hint interface{} + + // Specifies parameters for the find one and delete expression. This option is only valid for MongoDB versions >= 5.0. Older + // servers will report an error for using this option. This must be a document mapping parameter names to values. + // Values must be constant or closed expressions that do not reference document fields. Parameters can then be + // accessed as variables in an aggregate expression context (e.g. "$$var"). + Let interface{} } // FindOneAndDelete creates a new FindOneAndDeleteOptions instance. @@ -927,6 +978,12 @@ func (f *FindOneAndDeleteOptions) SetHint(hint interface{}) *FindOneAndDeleteOpt return f } +// SetLet sets the value for the Let field. +func (f *FindOneAndDeleteOptions) SetLet(let interface{}) *FindOneAndDeleteOptions { + f.Let = let + return f +} + // MergeFindOneAndDeleteOptions combines the given FindOneAndDeleteOptions instances into a single // FindOneAndDeleteOptions in a last-one-wins fashion. func MergeFindOneAndDeleteOptions(opts ...*FindOneAndDeleteOptions) *FindOneAndDeleteOptions { @@ -950,6 +1007,9 @@ func MergeFindOneAndDeleteOptions(opts ...*FindOneAndDeleteOptions) *FindOneAndD if opt.Hint != nil { fo.Hint = opt.Hint } + if opt.Let != nil { + fo.Let = opt.Let + } } return fo diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/indexoptions.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/indexoptions.go index 076eedd7..ed71fb41 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/indexoptions.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/options/indexoptions.go @@ -389,6 +389,9 @@ func MergeIndexOptions(opts ...*IndexOptions) *IndexOptions { i := Index() for _, opt := range opts { + if opt == nil { + continue + } if opt.Background != nil { i.Background = opt.Background } diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/listcollectionsoptions.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/listcollectionsoptions.go index 4c2ce3e6..6f4b1cca 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/listcollectionsoptions.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/options/listcollectionsoptions.go @@ -13,6 +13,10 @@ type ListCollectionsOptions struct { // The maximum number of documents to be included in each batch returned by the server. BatchSize *int32 + + // If true, and NameOnly is true, limits the documents returned to only contain collections the user is authorized to use. The default value + // is false. This option is only valid for MongoDB server versions >= 4.0. Server versions < 4.0 ignore this option. + AuthorizedCollections *bool } // ListCollections creates a new ListCollectionsOptions instance. @@ -32,6 +36,13 @@ func (lc *ListCollectionsOptions) SetBatchSize(size int32) *ListCollectionsOptio return lc } +// SetAuthorizedCollections sets the value for the AuthorizedCollections field. This option is only valid for MongoDB server versions >= 4.0. Server +// versions < 4.0 ignore this option. +func (lc *ListCollectionsOptions) SetAuthorizedCollections(b bool) *ListCollectionsOptions { + lc.AuthorizedCollections = &b + return lc +} + // MergeListCollectionsOptions combines the given ListCollectionsOptions instances into a single *ListCollectionsOptions // in a last-one-wins fashion. func MergeListCollectionsOptions(opts ...*ListCollectionsOptions) *ListCollectionsOptions { @@ -46,6 +57,9 @@ func MergeListCollectionsOptions(opts ...*ListCollectionsOptions) *ListCollectio if opt.BatchSize != nil { lc.BatchSize = opt.BatchSize } + if opt.AuthorizedCollections != nil { + lc.AuthorizedCollections = opt.AuthorizedCollections + } } return lc diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/listdatabasesoptions.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/listdatabasesoptions.go index 42fe1704..f19d89c6 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/listdatabasesoptions.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/options/listdatabasesoptions.go @@ -40,7 +40,7 @@ func (ld *ListDatabasesOptions) SetAuthorizedDatabases(b bool) *ListDatabasesOpt func MergeListDatabasesOptions(opts ...*ListDatabasesOptions) *ListDatabasesOptions { ld := ListDatabases() for _, opt := range opts { - if opts == nil { + if opt == nil { continue } if opt.NameOnly != nil { diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/mongooptions.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/mongooptions.go index f6df2437..179b7356 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/mongooptions.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/options/mongooptions.go @@ -22,7 +22,7 @@ type Collation struct { Locale string `bson:",omitempty"` // The locale CaseLevel bool `bson:",omitempty"` // The case level CaseFirst string `bson:",omitempty"` // The case ordering - Strength int `bson:",omitempty"` // The number of comparision levels to use + Strength int `bson:",omitempty"` // The number of comparison levels to use NumericOrdering bool `bson:",omitempty"` // Whether to order numbers based on numerical order and not collation order Alternate string `bson:",omitempty"` // Whether spaces and punctuation are considered base characters MaxVariable string `bson:",omitempty"` // Which characters are affected by alternate: "shifted" @@ -157,5 +157,3 @@ type MarshalError struct { func (me MarshalError) Error() string { return fmt.Sprintf("cannot transform type %s to a bson.Raw", reflect.TypeOf(me.Value)) } - -var defaultRegistry = bson.DefaultRegistry diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/replaceoptions.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/replaceoptions.go index 543c81ce..9cb9ab87 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/replaceoptions.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/options/replaceoptions.go @@ -30,6 +30,12 @@ type ReplaceOptions struct { // If true, a new document will be inserted if the filter does not match any documents in the collection. The // default value is false. Upsert *bool + + // Specifies parameters for the aggregate expression. This option is only valid for MongoDB versions >= 5.0. Older + // servers will report an error for using this option. This must be a document mapping parameter names to values. + // Values must be constant or closed expressions that do not reference document fields. Parameters can then be + // accessed as variables in an aggregate expression context (e.g. "$$var"). + Let interface{} } // Replace creates a new ReplaceOptions instance. @@ -61,6 +67,12 @@ func (ro *ReplaceOptions) SetUpsert(b bool) *ReplaceOptions { return ro } +// SetLet sets the value for the Let field. +func (ro *ReplaceOptions) SetLet(l interface{}) *ReplaceOptions { + ro.Let = l + return ro +} + // MergeReplaceOptions combines the given ReplaceOptions instances into a single ReplaceOptions in a last-one-wins // fashion. func MergeReplaceOptions(opts ...*ReplaceOptions) *ReplaceOptions { @@ -81,6 +93,9 @@ func MergeReplaceOptions(opts ...*ReplaceOptions) *ReplaceOptions { if ro.Upsert != nil { rOpts.Upsert = ro.Upsert } + if ro.Let != nil { + rOpts.Let = ro.Let + } } return rOpts diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/options/updateoptions.go b/vendor/go.mongodb.org/mongo-driver/mongo/options/updateoptions.go index 4d1e7e47..fd0631de 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/options/updateoptions.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/options/updateoptions.go @@ -35,6 +35,12 @@ type UpdateOptions struct { // If true, a new document will be inserted if the filter does not match any documents in the collection. The // default value is false. Upsert *bool + + // Specifies parameters for the update expression. This option is only valid for MongoDB versions >= 5.0. Older + // servers will report an error for using this option. This must be a document mapping parameter names to values. + // Values must be constant or closed expressions that do not reference document fields. Parameters can then be + // accessed as variables in an aggregate expression context (e.g. "$$var"). + Let interface{} } // Update creates a new UpdateOptions instance. @@ -72,6 +78,12 @@ func (uo *UpdateOptions) SetUpsert(b bool) *UpdateOptions { return uo } +// SetLet sets the value for the Let field. +func (uo *UpdateOptions) SetLet(l interface{}) *UpdateOptions { + uo.Let = l + return uo +} + // MergeUpdateOptions combines the given UpdateOptions instances into a single UpdateOptions in a last-one-wins fashion. func MergeUpdateOptions(opts ...*UpdateOptions) *UpdateOptions { uOpts := Update() @@ -94,6 +106,9 @@ func MergeUpdateOptions(opts ...*UpdateOptions) *UpdateOptions { if uo.Upsert != nil { uOpts.Upsert = uo.Upsert } + if uo.Let != nil { + uOpts.Let = uo.Let + } } return uOpts diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/readpref/options.go b/vendor/go.mongodb.org/mongo-driver/mongo/readpref/options.go index f742de0c..68286d10 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/readpref/options.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/readpref/options.go @@ -61,7 +61,7 @@ func WithTagSets(tagSets ...tag.Set) Option { // WithHedgeEnabled specifies whether or not hedged reads should be enabled in the server. This feature requires MongoDB // server version 4.4 or higher. For more information about hedged reads, see -// https://docs.mongodb.com/master/core/sharded-cluster-query-router/#mongos-hedged-reads. If not specified, the default +// https://docs.mongodb.com/manual/core/sharded-cluster-query-router/#mongos-hedged-reads. If not specified, the default // is to not send a value to the server, which will result in the server defaults being used. func WithHedgeEnabled(hedgeEnabled bool) Option { return func(rp *ReadPref) error { diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/readpref/readpref.go b/vendor/go.mongodb.org/mongo-driver/mongo/readpref/readpref.go index b651b69e..a3ac0edf 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/readpref/readpref.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/readpref/readpref.go @@ -65,6 +65,9 @@ func New(mode Mode, opts ...Option) (*ReadPref, error) { } for _, opt := range opts { + if opt == nil { + continue + } err := opt(rp) if err != nil { return nil, err diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/results.go b/vendor/go.mongodb.org/mongo-driver/mongo/results.go index 52e2dedd..6951bea6 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/results.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/results.go @@ -158,15 +158,30 @@ type IndexSpecification struct { // The index version. Version int32 + + // The length of time, in seconds, for documents to remain in the collection. The default value is 0, which means + // that documents will remain in the collection until they're explicitly deleted or the collection is dropped. + ExpireAfterSeconds *int32 + + // If true, the index will only reference documents that contain the fields specified in the index. The default is + // false. + Sparse *bool + + // If true, the collection will not accept insertion or update of documents where the index key value matches an + // existing value in the index. The default is false. + Unique *bool } var _ bson.Unmarshaler = (*IndexSpecification)(nil) type unmarshalIndexSpecification struct { - Name string `bson:"name"` - Namespace string `bson:"ns"` - KeysDocument bson.Raw `bson:"key"` - Version int32 `bson:"v"` + Name string `bson:"name"` + Namespace string `bson:"ns"` + KeysDocument bson.Raw `bson:"key"` + Version int32 `bson:"v"` + ExpireAfterSeconds *int32 `bson:"expireAfterSeconds"` + Sparse *bool `bson:"sparse"` + Unique *bool `bson:"unique"` } // UnmarshalBSON implements the bson.Unmarshaler interface. @@ -180,6 +195,9 @@ func (i *IndexSpecification) UnmarshalBSON(data []byte) error { i.Namespace = temp.Namespace i.KeysDocument = temp.KeysDocument i.Version = temp.Version + i.ExpireAfterSeconds = temp.ExpireAfterSeconds + i.Sparse = temp.Sparse + i.Unique = temp.Unique return nil } diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/session.go b/vendor/go.mongodb.org/mongo-driver/mongo/session.go index c1a2c8ea..93bc5cb4 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/session.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/session.go @@ -100,13 +100,16 @@ func SessionFromContext(ctx context.Context) Session { // resources are properly cleaned up, context deadlines and cancellations will not be respected during this call. For a // usage example, see the Client.StartSession method documentation. // -// ClusterTime, OperationTime, Client, and ID return the session's current operation time, the session's current cluster +// ClusterTime, OperationTime, Client, and ID return the session's current cluster time, the session's current operation // time, the Client associated with the session, and the ID document associated with the session, respectively. The ID // document for a session is in the form {"id": }. // // EndSession method should abort any existing transactions and close the session. // -// AdvanceClusterTime and AdvanceOperationTime are for internal use only and must not be called. +// AdvanceClusterTime advances the cluster time for a session. This method will return an error if the session has ended. +// +// AdvanceOperationTime advances the operation time for a session. This method will return an error if the session has +// ended. type Session interface { // Functions to modify session state. StartTransaction(...*options.TransactionOptions) error diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/single_result.go b/vendor/go.mongodb.org/mongo-driver/mongo/single_result.go index c693dfae..47602502 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/single_result.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/single_result.go @@ -28,6 +28,31 @@ type SingleResult struct { reg *bsoncodec.Registry } +// NewSingleResultFromDocument creates a SingleResult with the provided error, registry, and an underlying Cursor pre-loaded with +// the provided document, error and registry. If no registry is provided, bson.DefaultRegistry will be used. If an error distinct +// from the one provided occurs during creation of the SingleResult, that error will be stored on the returned SingleResult. +// +// The document parameter must be a non-nil document. +func NewSingleResultFromDocument(document interface{}, err error, registry *bsoncodec.Registry) *SingleResult { + if document == nil { + return &SingleResult{err: ErrNilDocument} + } + if registry == nil { + registry = bson.DefaultRegistry + } + + cur, createErr := NewCursorFromDocuments([]interface{}{document}, err, registry) + if createErr != nil { + return &SingleResult{err: createErr} + } + + return &SingleResult{ + cur: cur, + err: err, + reg: registry, + } +} + // Decode will unmarshal the document represented by this SingleResult into v. If there was an error from the operation // that created this SingleResult, that error will be returned. If the operation returned no documents, Decode will // return ErrNoDocuments. @@ -71,6 +96,7 @@ func (sr *SingleResult) setRdrContents() error { return nil case sr.cur != nil: defer sr.cur.Close(context.TODO()) + if !sr.cur.Next(context.TODO()) { if err := sr.cur.Err(); err != nil { return err diff --git a/vendor/go.mongodb.org/mongo-driver/mongo/writeconcern/writeconcern.go b/vendor/go.mongodb.org/mongo-driver/mongo/writeconcern/writeconcern.go index b2831bf1..b9145c9e 100644 --- a/vendor/go.mongodb.org/mongo-driver/mongo/writeconcern/writeconcern.go +++ b/vendor/go.mongodb.org/mongo-driver/mongo/writeconcern/writeconcern.go @@ -105,7 +105,7 @@ func (wc *WriteConcern) MarshalBSONValue() (bsontype.Type, []byte, error) { elems = bsoncore.AppendInt32Element(elems, "w", int32(t)) case string: - elems = bsoncore.AppendStringElement(elems, "w", string(t)) + elems = bsoncore.AppendStringElement(elems, "w", t) } } diff --git a/vendor/go.mongodb.org/mongo-driver/version/version.go b/vendor/go.mongodb.org/mongo-driver/version/version.go index b964a44a..3adbbb66 100644 --- a/vendor/go.mongodb.org/mongo-driver/version/version.go +++ b/vendor/go.mongodb.org/mongo-driver/version/version.go @@ -7,4 +7,4 @@ package version // import "go.mongodb.org/mongo-driver/version" // Driver is the current version of the driver. -var Driver = "v1.7.4" +var Driver = "v1.9.1" diff --git a/vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/bson_documentbuilder.go b/vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/bson_documentbuilder.go index b0d45212..52162f8a 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/bson_documentbuilder.go +++ b/vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/bson_documentbuilder.go @@ -45,7 +45,7 @@ func (db *DocumentBuilder) AppendInt32(key string, i32 int32) *DocumentBuilder { return db } -// AppendDocument will append a bson embeded document element using key +// AppendDocument will append a bson embedded document element using key // and doc to DocumentBuilder.doc func (db *DocumentBuilder) AppendDocument(key string, doc []byte) *DocumentBuilder { db.doc = AppendDocumentElement(db.doc, key, doc) diff --git a/vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/bsoncore.go b/vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/bsoncore.go index b8db838a..5b0c3a04 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/bsoncore.go +++ b/vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/bsoncore.go @@ -15,7 +15,7 @@ // enough bytes. This library attempts to do no validation, it will only return // false if there are not enough bytes for an item to be read. For example, the // ReadDocument function checks the length, if that length is larger than the -// number of bytes availble, it will return false, if there are enough bytes, it +// number of bytes available, it will return false, if there are enough bytes, it // will return those bytes and true. It is the consumers responsibility to // validate those bytes. // @@ -69,7 +69,7 @@ func AppendHeader(dst []byte, t bsontype.Type, key string) []byte { // was read. // ReadType will return the first byte of the provided []byte as a type. If -// there is no availble byte, false is returned. +// there is no available byte, false is returned. func ReadType(src []byte) (bsontype.Type, []byte, bool) { if len(src) < 1 { return 0, src, false @@ -231,7 +231,7 @@ func AppendDocumentEnd(dst []byte, index int32) ([]byte, error) { // AppendDocument will append doc to dst and return the extended buffer. func AppendDocument(dst []byte, doc []byte) []byte { return append(dst, doc...) } -// AppendDocumentElement will append a BSON embeded document element using key +// AppendDocumentElement will append a BSON embedded document element using key // and doc to dst and return the extended buffer. func AppendDocumentElement(dst []byte, key string, doc []byte) []byte { return AppendDocument(AppendHeader(dst, bsontype.EmbeddedDocument, key), doc) diff --git a/vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/value.go b/vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/value.go index ed95233d..54aa617c 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/value.go +++ b/vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/value.go @@ -602,7 +602,7 @@ func (v Value) Time() time.Time { if !ok { panic(NewInsufficientBytesError(v.Data, v.Data)) } - return time.Unix(int64(dt)/1000, int64(dt)%1000*1000000) + return time.Unix(dt/1000, dt%1000*1000000) } // TimeOK is the same as Time, except it returns a boolean instead of @@ -615,7 +615,7 @@ func (v Value) TimeOK() (time.Time, bool) { if !ok { return time.Time{}, false } - return time.Unix(int64(dt)/1000, int64(dt)%1000*1000000), true + return time.Unix(dt/1000, dt%1000*1000000), true } // Regex returns the BSON regex value the Value represents. It panics if the value is a BSON diff --git a/vendor/go.mongodb.org/mongo-driver/x/bsonx/document.go b/vendor/go.mongodb.org/mongo-driver/x/bsonx/document.go index 6d2c6f31..2d53bc18 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/bsonx/document.go +++ b/vendor/go.mongodb.org/mongo-driver/x/bsonx/document.go @@ -264,7 +264,7 @@ func (d Doc) Equal(id IDoc) bool { } } case MDoc: - unique := make(map[string]struct{}, 0) + unique := make(map[string]struct{}) for _, elem := range d { unique[elem.Key] = struct{}{} val, ok := tt[elem.Key] diff --git a/vendor/go.mongodb.org/mongo-driver/x/bsonx/element.go b/vendor/go.mongodb.org/mongo-driver/x/bsonx/element.go index 66054b2c..00d1ba37 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/bsonx/element.go +++ b/vendor/go.mongodb.org/mongo-driver/x/bsonx/element.go @@ -12,8 +12,6 @@ import ( "go.mongodb.org/mongo-driver/bson/bsontype" ) -const validateMaxDepthDefault = 2048 - // ElementTypeError specifies that a method to obtain a BSON value an incorrect type was called on a bson.Value. // // TODO: rename this ValueTypeError. diff --git a/vendor/go.mongodb.org/mongo-driver/x/bsonx/mdocument.go b/vendor/go.mongodb.org/mongo-driver/x/bsonx/mdocument.go index 380c8629..7877f224 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/bsonx/mdocument.go +++ b/vendor/go.mongodb.org/mongo-driver/x/bsonx/mdocument.go @@ -21,7 +21,7 @@ type MDoc map[string]Val // ReadMDoc will create a Doc using the provided slice of bytes. If the // slice of bytes is not a valid BSON document, this method will return an error. func ReadMDoc(b []byte) (MDoc, error) { - doc := make(MDoc, 0) + doc := make(MDoc) err := doc.UnmarshalBSON(b) if err != nil { return nil, err @@ -188,7 +188,7 @@ func (d MDoc) Equal(id IDoc) bool { } } case Doc: - unique := make(map[string]struct{}, 0) + unique := make(map[string]struct{}) for _, elem := range tt { unique[elem.Key] = struct{}{} val, ok := d[elem.Key] diff --git a/vendor/go.mongodb.org/mongo-driver/x/bsonx/primitive_codecs.go b/vendor/go.mongodb.org/mongo-driver/x/bsonx/primitive_codecs.go index 7328a867..01bd1826 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/bsonx/primitive_codecs.go +++ b/vendor/go.mongodb.org/mongo-driver/x/bsonx/primitive_codecs.go @@ -19,7 +19,6 @@ import ( var primitiveCodecs PrimitiveCodecs var tDocument = reflect.TypeOf((Doc)(nil)) -var tMDoc = reflect.TypeOf((MDoc)(nil)) var tArray = reflect.TypeOf((Arr)(nil)) var tValue = reflect.TypeOf(Val{}) var tElementSlice = reflect.TypeOf(([]Elem)(nil)) diff --git a/vendor/go.mongodb.org/mongo-driver/x/bsonx/reflectionfree_d_codec.go b/vendor/go.mongodb.org/mongo-driver/x/bsonx/reflectionfree_d_codec.go index 9df93ad4..7e68e55c 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/bsonx/reflectionfree_d_codec.go +++ b/vendor/go.mongodb.org/mongo-driver/x/bsonx/reflectionfree_d_codec.go @@ -20,7 +20,6 @@ import ( var ( tPrimitiveD = reflect.TypeOf(primitive.D{}) - tPrimitiveA = reflect.TypeOf(primitive.A{}) tPrimitiveCWS = reflect.TypeOf(primitive.CodeWithScope{}) defaultValueEncoders = bsoncodec.DefaultValueEncoders{} defaultValueDecoders = bsoncodec.DefaultValueDecoders{} @@ -224,13 +223,13 @@ func (r *reflectionFreeDCodec) decodeValue(dc bsoncodec.DecodeContext, vr bsonrw func (r *reflectionFreeDCodec) encodeDocumentValue(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, v interface{}) error { switch val := v.(type) { case int: - return r.encodeInt(ec, vw, val) + return r.encodeInt(vw, val) case int8: return vw.WriteInt32(int32(val)) case int16: return vw.WriteInt32(int32(val)) case int32: - return vw.WriteInt32(int32(val)) + return vw.WriteInt32(val) case int64: return r.encodeInt64(ec, vw, val) case uint: @@ -293,69 +292,69 @@ func (r *reflectionFreeDCodec) encodeDocumentValue(ec bsoncodec.EncodeContext, v case []primitive.D: return r.encodeSliceD(ec, vw, val) case []int: - return r.encodeSliceInt(ec, vw, val) + return r.encodeSliceInt(vw, val) case []int8: - return r.encodeSliceInt8(ec, vw, val) + return r.encodeSliceInt8(vw, val) case []int16: - return r.encodeSliceInt16(ec, vw, val) + return r.encodeSliceInt16(vw, val) case []int32: - return r.encodeSliceInt32(ec, vw, val) + return r.encodeSliceInt32(vw, val) case []int64: return r.encodeSliceInt64(ec, vw, val) case []uint: return r.encodeSliceUint(ec, vw, val) case []uint16: - return r.encodeSliceUint16(ec, vw, val) + return r.encodeSliceUint16(vw, val) case []uint32: return r.encodeSliceUint32(ec, vw, val) case []uint64: return r.encodeSliceUint64(ec, vw, val) case [][]byte: - return r.encodeSliceByteSlice(ec, vw, val) + return r.encodeSliceByteSlice(vw, val) case []primitive.Binary: - return r.encodeSliceBinary(ec, vw, val) + return r.encodeSliceBinary(vw, val) case []bool: - return r.encodeSliceBoolean(ec, vw, val) + return r.encodeSliceBoolean(vw, val) case []primitive.CodeWithScope: return r.encodeSliceCWS(ec, vw, val) case []primitive.DBPointer: - return r.encodeSliceDBPointer(ec, vw, val) + return r.encodeSliceDBPointer(vw, val) case []primitive.DateTime: - return r.encodeSliceDateTime(ec, vw, val) + return r.encodeSliceDateTime(vw, val) case []time.Time: - return r.encodeSliceTimeTime(ec, vw, val) + return r.encodeSliceTimeTime(vw, val) case []primitive.Decimal128: - return r.encodeSliceDecimal128(ec, vw, val) + return r.encodeSliceDecimal128(vw, val) case []float32: - return r.encodeSliceFloat32(ec, vw, val) + return r.encodeSliceFloat32(vw, val) case []float64: - return r.encodeSliceFloat64(ec, vw, val) + return r.encodeSliceFloat64(vw, val) case []primitive.JavaScript: - return r.encodeSliceJavaScript(ec, vw, val) + return r.encodeSliceJavaScript(vw, val) case []primitive.MinKey: - return r.encodeSliceMinKey(ec, vw, val) + return r.encodeSliceMinKey(vw, val) case []primitive.MaxKey: - return r.encodeSliceMaxKey(ec, vw, val) + return r.encodeSliceMaxKey(vw, val) case []primitive.Null: - return r.encodeSliceNull(ec, vw, val) + return r.encodeSliceNull(vw, val) case []primitive.ObjectID: - return r.encodeSliceObjectID(ec, vw, val) + return r.encodeSliceObjectID(vw, val) case []primitive.Regex: - return r.encodeSliceRegex(ec, vw, val) + return r.encodeSliceRegex(vw, val) case []string: - return r.encodeSliceString(ec, vw, val) + return r.encodeSliceString(vw, val) case []primitive.Symbol: - return r.encodeSliceSymbol(ec, vw, val) + return r.encodeSliceSymbol(vw, val) case []primitive.Timestamp: - return r.encodeSliceTimestamp(ec, vw, val) + return r.encodeSliceTimestamp(vw, val) case []primitive.Undefined: - return r.encodeSliceUndefined(ec, vw, val) + return r.encodeSliceUndefined(vw, val) default: return fmt.Errorf("value of type %T not supported", v) } } -func (r *reflectionFreeDCodec) encodeInt(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val int) error { +func (r *reflectionFreeDCodec) encodeInt(vw bsonrw.ValueWriter, val int) error { if fitsIn32Bits(int64(val)) { return vw.WriteInt32(int32(val)) } @@ -366,7 +365,7 @@ func (r *reflectionFreeDCodec) encodeInt64(ec bsoncodec.EncodeContext, vw bsonrw if ec.MinSize && fitsIn32Bits(val) { return vw.WriteInt32(int32(val)) } - return vw.WriteInt64(int64(val)) + return vw.WriteInt64(val) } func (r *reflectionFreeDCodec) encodeUint64(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val uint64) error { @@ -400,7 +399,7 @@ func (r *reflectionFreeDCodec) encodeDocument(ec bsoncodec.EncodeContext, vw bso return dw.WriteDocumentEnd() } -func (r *reflectionFreeDCodec) encodeSliceByteSlice(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr [][]byte) error { +func (r *reflectionFreeDCodec) encodeSliceByteSlice(vw bsonrw.ValueWriter, arr [][]byte) error { aw, err := vw.WriteArray() if err != nil { return err @@ -420,7 +419,7 @@ func (r *reflectionFreeDCodec) encodeSliceByteSlice(ec bsoncodec.EncodeContext, return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceBinary(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []primitive.Binary) error { +func (r *reflectionFreeDCodec) encodeSliceBinary(vw bsonrw.ValueWriter, arr []primitive.Binary) error { aw, err := vw.WriteArray() if err != nil { return err @@ -440,7 +439,7 @@ func (r *reflectionFreeDCodec) encodeSliceBinary(ec bsoncodec.EncodeContext, vw return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceBoolean(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []bool) error { +func (r *reflectionFreeDCodec) encodeSliceBoolean(vw bsonrw.ValueWriter, arr []bool) error { aw, err := vw.WriteArray() if err != nil { return err @@ -480,7 +479,7 @@ func (r *reflectionFreeDCodec) encodeSliceCWS(ec bsoncodec.EncodeContext, vw bso return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceDBPointer(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []primitive.DBPointer) error { +func (r *reflectionFreeDCodec) encodeSliceDBPointer(vw bsonrw.ValueWriter, arr []primitive.DBPointer) error { aw, err := vw.WriteArray() if err != nil { return err @@ -500,7 +499,7 @@ func (r *reflectionFreeDCodec) encodeSliceDBPointer(ec bsoncodec.EncodeContext, return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceDateTime(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []primitive.DateTime) error { +func (r *reflectionFreeDCodec) encodeSliceDateTime(vw bsonrw.ValueWriter, arr []primitive.DateTime) error { aw, err := vw.WriteArray() if err != nil { return err @@ -520,7 +519,7 @@ func (r *reflectionFreeDCodec) encodeSliceDateTime(ec bsoncodec.EncodeContext, v return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceTimeTime(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []time.Time) error { +func (r *reflectionFreeDCodec) encodeSliceTimeTime(vw bsonrw.ValueWriter, arr []time.Time) error { aw, err := vw.WriteArray() if err != nil { return err @@ -541,7 +540,7 @@ func (r *reflectionFreeDCodec) encodeSliceTimeTime(ec bsoncodec.EncodeContext, v return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceDecimal128(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []primitive.Decimal128) error { +func (r *reflectionFreeDCodec) encodeSliceDecimal128(vw bsonrw.ValueWriter, arr []primitive.Decimal128) error { aw, err := vw.WriteArray() if err != nil { return err @@ -561,7 +560,7 @@ func (r *reflectionFreeDCodec) encodeSliceDecimal128(ec bsoncodec.EncodeContext, return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceFloat32(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []float32) error { +func (r *reflectionFreeDCodec) encodeSliceFloat32(vw bsonrw.ValueWriter, arr []float32) error { aw, err := vw.WriteArray() if err != nil { return err @@ -581,7 +580,7 @@ func (r *reflectionFreeDCodec) encodeSliceFloat32(ec bsoncodec.EncodeContext, vw return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceFloat64(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []float64) error { +func (r *reflectionFreeDCodec) encodeSliceFloat64(vw bsonrw.ValueWriter, arr []float64) error { aw, err := vw.WriteArray() if err != nil { return err @@ -601,7 +600,7 @@ func (r *reflectionFreeDCodec) encodeSliceFloat64(ec bsoncodec.EncodeContext, vw return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceJavaScript(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []primitive.JavaScript) error { +func (r *reflectionFreeDCodec) encodeSliceJavaScript(vw bsonrw.ValueWriter, arr []primitive.JavaScript) error { aw, err := vw.WriteArray() if err != nil { return err @@ -621,7 +620,7 @@ func (r *reflectionFreeDCodec) encodeSliceJavaScript(ec bsoncodec.EncodeContext, return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceMinKey(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []primitive.MinKey) error { +func (r *reflectionFreeDCodec) encodeSliceMinKey(vw bsonrw.ValueWriter, arr []primitive.MinKey) error { aw, err := vw.WriteArray() if err != nil { return err @@ -641,7 +640,7 @@ func (r *reflectionFreeDCodec) encodeSliceMinKey(ec bsoncodec.EncodeContext, vw return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceMaxKey(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []primitive.MaxKey) error { +func (r *reflectionFreeDCodec) encodeSliceMaxKey(vw bsonrw.ValueWriter, arr []primitive.MaxKey) error { aw, err := vw.WriteArray() if err != nil { return err @@ -661,7 +660,7 @@ func (r *reflectionFreeDCodec) encodeSliceMaxKey(ec bsoncodec.EncodeContext, vw return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceNull(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []primitive.Null) error { +func (r *reflectionFreeDCodec) encodeSliceNull(vw bsonrw.ValueWriter, arr []primitive.Null) error { aw, err := vw.WriteArray() if err != nil { return err @@ -681,7 +680,7 @@ func (r *reflectionFreeDCodec) encodeSliceNull(ec bsoncodec.EncodeContext, vw bs return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceObjectID(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []primitive.ObjectID) error { +func (r *reflectionFreeDCodec) encodeSliceObjectID(vw bsonrw.ValueWriter, arr []primitive.ObjectID) error { aw, err := vw.WriteArray() if err != nil { return err @@ -701,7 +700,7 @@ func (r *reflectionFreeDCodec) encodeSliceObjectID(ec bsoncodec.EncodeContext, v return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceRegex(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []primitive.Regex) error { +func (r *reflectionFreeDCodec) encodeSliceRegex(vw bsonrw.ValueWriter, arr []primitive.Regex) error { aw, err := vw.WriteArray() if err != nil { return err @@ -721,7 +720,7 @@ func (r *reflectionFreeDCodec) encodeSliceRegex(ec bsoncodec.EncodeContext, vw b return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceString(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []string) error { +func (r *reflectionFreeDCodec) encodeSliceString(vw bsonrw.ValueWriter, arr []string) error { aw, err := vw.WriteArray() if err != nil { return err @@ -741,7 +740,7 @@ func (r *reflectionFreeDCodec) encodeSliceString(ec bsoncodec.EncodeContext, vw return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceSymbol(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []primitive.Symbol) error { +func (r *reflectionFreeDCodec) encodeSliceSymbol(vw bsonrw.ValueWriter, arr []primitive.Symbol) error { aw, err := vw.WriteArray() if err != nil { return err @@ -761,7 +760,7 @@ func (r *reflectionFreeDCodec) encodeSliceSymbol(ec bsoncodec.EncodeContext, vw return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceTimestamp(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []primitive.Timestamp) error { +func (r *reflectionFreeDCodec) encodeSliceTimestamp(vw bsonrw.ValueWriter, arr []primitive.Timestamp) error { aw, err := vw.WriteArray() if err != nil { return err @@ -781,7 +780,7 @@ func (r *reflectionFreeDCodec) encodeSliceTimestamp(ec bsoncodec.EncodeContext, return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceUndefined(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []primitive.Undefined) error { +func (r *reflectionFreeDCodec) encodeSliceUndefined(vw bsonrw.ValueWriter, arr []primitive.Undefined) error { aw, err := vw.WriteArray() if err != nil { return err @@ -841,7 +840,7 @@ func (r *reflectionFreeDCodec) encodeSliceD(ec bsoncodec.EncodeContext, vw bsonr return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceInt(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []int) error { +func (r *reflectionFreeDCodec) encodeSliceInt(vw bsonrw.ValueWriter, arr []int) error { aw, err := vw.WriteArray() if err != nil { return err @@ -853,7 +852,7 @@ func (r *reflectionFreeDCodec) encodeSliceInt(ec bsoncodec.EncodeContext, vw bso return err } - if err := r.encodeInt(ec, arrayValWriter, val); err != nil { + if err := r.encodeInt(arrayValWriter, val); err != nil { return err } } @@ -861,7 +860,7 @@ func (r *reflectionFreeDCodec) encodeSliceInt(ec bsoncodec.EncodeContext, vw bso return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceInt8(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []int8) error { +func (r *reflectionFreeDCodec) encodeSliceInt8(vw bsonrw.ValueWriter, arr []int8) error { aw, err := vw.WriteArray() if err != nil { return err @@ -881,7 +880,7 @@ func (r *reflectionFreeDCodec) encodeSliceInt8(ec bsoncodec.EncodeContext, vw bs return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceInt16(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []int16) error { +func (r *reflectionFreeDCodec) encodeSliceInt16(vw bsonrw.ValueWriter, arr []int16) error { aw, err := vw.WriteArray() if err != nil { return err @@ -901,7 +900,7 @@ func (r *reflectionFreeDCodec) encodeSliceInt16(ec bsoncodec.EncodeContext, vw b return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceInt32(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []int32) error { +func (r *reflectionFreeDCodec) encodeSliceInt32(vw bsonrw.ValueWriter, arr []int32) error { aw, err := vw.WriteArray() if err != nil { return err @@ -961,7 +960,7 @@ func (r *reflectionFreeDCodec) encodeSliceUint(ec bsoncodec.EncodeContext, vw bs return aw.WriteArrayEnd() } -func (r *reflectionFreeDCodec) encodeSliceUint16(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []uint16) error { +func (r *reflectionFreeDCodec) encodeSliceUint16(vw bsonrw.ValueWriter, arr []uint16) error { aw, err := vw.WriteArray() if err != nil { return err diff --git a/vendor/go.mongodb.org/mongo-driver/x/bsonx/registry.go b/vendor/go.mongodb.org/mongo-driver/x/bsonx/registry.go index ac21df22..7518bc04 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/bsonx/registry.go +++ b/vendor/go.mongodb.org/mongo-driver/x/bsonx/registry.go @@ -10,7 +10,7 @@ import ( var DefaultRegistry = NewRegistryBuilder().Build() // NewRegistryBuilder creates a new RegistryBuilder configured with the default encoders and -// deocders from the bsoncodec.DefaultValueEncoders and bsoncodec.DefaultValueDecoders types and the +// decoders from the bsoncodec.DefaultValueEncoders and bsoncodec.DefaultValueDecoders types and the // PrimitiveCodecs type in this package. func NewRegistryBuilder() *bsoncodec.RegistryBuilder { rb := bsoncodec.NewRegistryBuilder() diff --git a/vendor/go.mongodb.org/mongo-driver/x/bsonx/value.go b/vendor/go.mongodb.org/mongo-driver/x/bsonx/value.go index aa7e9e6b..f66f6b24 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/bsonx/value.go +++ b/vendor/go.mongodb.org/mongo-driver/x/bsonx/value.go @@ -30,33 +30,12 @@ type Val struct { primitive interface{} } -func (v Val) reset() Val { - v.primitive = nil // clear out any pointers so we don't accidentally stop them from being garbage collected. - v.t = bsontype.Type(0) - v.bootstrap[0] = 0x00 - v.bootstrap[1] = 0x00 - v.bootstrap[2] = 0x00 - v.bootstrap[3] = 0x00 - v.bootstrap[4] = 0x00 - v.bootstrap[5] = 0x00 - v.bootstrap[6] = 0x00 - v.bootstrap[7] = 0x00 - v.bootstrap[8] = 0x00 - v.bootstrap[9] = 0x00 - v.bootstrap[10] = 0x00 - v.bootstrap[11] = 0x00 - v.bootstrap[12] = 0x00 - v.bootstrap[13] = 0x00 - v.bootstrap[14] = 0x00 - return v -} - func (v Val) string() string { if v.primitive != nil { return v.primitive.(string) } // The string will either end with a null byte or it fills the entire bootstrap space. - length := uint8(v.bootstrap[0]) + length := v.bootstrap[0] return string(v.bootstrap[1 : length+1]) } @@ -190,7 +169,7 @@ func (v Val) MarshalAppendBSONValue(dst []byte) (bsontype.Type, []byte, error) { case bsontype.Boolean: dst = bsoncore.AppendBoolean(dst, v.Boolean()) case bsontype.DateTime: - dst = bsoncore.AppendDateTime(dst, int64(v.DateTime())) + dst = bsoncore.AppendDateTime(dst, v.DateTime()) case bsontype.Null: case bsontype.Regex: pattern, options := v.Regex() @@ -199,9 +178,9 @@ func (v Val) MarshalAppendBSONValue(dst []byte) (bsontype.Type, []byte, error) { ns, ptr := v.DBPointer() dst = bsoncore.AppendDBPointer(dst, ns, ptr) case bsontype.JavaScript: - dst = bsoncore.AppendJavaScript(dst, string(v.JavaScript())) + dst = bsoncore.AppendJavaScript(dst, v.JavaScript()) case bsontype.Symbol: - dst = bsoncore.AppendSymbol(dst, string(v.Symbol())) + dst = bsoncore.AppendSymbol(dst, v.Symbol()) case bsontype.CodeWithScope: code, doc := v.CodeWithScope() var scope []byte @@ -491,16 +470,12 @@ func (v Val) Undefined() { if v.t != bsontype.Undefined { panic(ElementTypeError{"bson.Value.Undefined", v.t}) } - return } // UndefinedOK is the same as Undefined, except it returns a boolean instead of // panicking. func (v Val) UndefinedOK() bool { - if v.t != bsontype.Undefined { - return false - } - return true + return v.t == bsontype.Undefined } // ObjectID returns the BSON ObjectID the Value represents. It panics if the value is a BSON type @@ -568,7 +543,7 @@ func (v Val) Time() time.Time { panic(ElementTypeError{"bson.Value.Time", v.t}) } i := v.i64() - return time.Unix(int64(i)/1000, int64(i)%1000*1000000) + return time.Unix(i/1000, i%1000*1000000) } // TimeOK is the same as Time, except it returns a boolean instead of @@ -578,7 +553,7 @@ func (v Val) TimeOK() (time.Time, bool) { return time.Time{}, false } i := v.i64() - return time.Unix(int64(i)/1000, int64(i)%1000*1000000), true + return time.Unix(i/1000, i%1000*1000000), true } // Null returns the BSON undefined the Value represents. It panics if the value is a BSON type @@ -587,7 +562,6 @@ func (v Val) Null() { if v.t != bsontype.Null && v.t != bsontype.Type(0) { panic(ElementTypeError{"bson.Value.Null", v.t}) } - return } // NullOK is the same as Null, except it returns a boolean instead of @@ -783,16 +757,12 @@ func (v Val) MinKey() { if v.t != bsontype.MinKey { panic(ElementTypeError{"bson.Value.MinKey", v.t}) } - return } // MinKeyOK is the same as MinKey, except it returns a boolean instead of // panicking. func (v Val) MinKeyOK() bool { - if v.t != bsontype.MinKey { - return false - } - return true + return v.t == bsontype.MinKey } // MaxKey returns the BSON maxkey the Value represents. It panics if the value is a BSON type @@ -801,16 +771,12 @@ func (v Val) MaxKey() { if v.t != bsontype.MaxKey { panic(ElementTypeError{"bson.Value.MaxKey", v.t}) } - return } // MaxKeyOK is the same as MaxKey, except it returns a boolean instead of // panicking. func (v Val) MaxKeyOK() bool { - if v.t != bsontype.MaxKey { - return false - } - return true + return v.t == bsontype.MaxKey } // Equal compares v to v2 and returns true if they are equal. Unknown BSON types are diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/DESIGN.md b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/DESIGN.md index 9342cb07..2fde89f8 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/DESIGN.md +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/DESIGN.md @@ -19,5 +19,5 @@ The `Operation` type handles executing a series of commands using a `Deployment` batch split write commands, such as insert. The type itself is heavily documented, so reading the code and comments together should provide an understanding of how the type works. -This type is not meant to be used directly by callers. Instead an wrapping type should be defined -using the IDL and an implementation generated using `operationgen`. +This type is not meant to be used directly by callers. Instead a wrapping type should be defined +using the IDL. diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/auth.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/auth.go index d0436904..d45dda61 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/auth.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/auth.go @@ -13,7 +13,6 @@ import ( "go.mongodb.org/mongo-driver/mongo/address" "go.mongodb.org/mongo-driver/mongo/description" - "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" "go.mongodb.org/mongo-driver/x/mongo/driver" "go.mongodb.org/mongo-driver/x/mongo/driver/operation" "go.mongodb.org/mongo-driver/x/mongo/driver/session" @@ -80,7 +79,7 @@ func (ah *authHandshaker) GetHandshakeInformation(ctx context.Context, addr addr return ah.wrapped.GetHandshakeInformation(ctx, addr, conn) } - op := operation.NewIsMaster(). + op := operation.NewHello(). AppName(ah.options.AppName). Compressors(ah.options.Compressors). SASLSupportedMechs(ah.options.DBUser). @@ -145,14 +144,14 @@ func (ah *authHandshaker) FinishHandshake(ctx context.Context, conn driver.Conne } func (ah *authHandshaker) authenticate(ctx context.Context, cfg *Config) error { - // If the initial isMaster reply included a response to the speculative authentication attempt, we only need to + // If the initial hello reply included a response to the speculative authentication attempt, we only need to // conduct the remainder of the conversation. if speculativeResponse := ah.handshakeInfo.SpeculativeAuthenticate; speculativeResponse != nil { // Defensively ensure that the server did not include a response if speculative auth was not attempted. if ah.conversation == nil { return errors.New("speculative auth was not attempted but the server included a response") } - return ah.conversation.Finish(ctx, cfg, bsoncore.Document(speculativeResponse)) + return ah.conversation.Finish(ctx, cfg, speculativeResponse) } // If the server does not support speculative authentication or the first attempt was not successful, we need to diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/aws_conv.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/aws_conv.go index fa0b0d0e..eb0032ce 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/aws_conv.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/aws_conv.go @@ -76,7 +76,7 @@ func (ac *awsConversation) Step(challenge []byte) (response []byte, err error) { switch ac.state { case clientStarting: ac.state = clientFirst - response, err = ac.firstMsg() + response = ac.firstMsg() case clientFirst: ac.state = clientFinal response, err = ac.finalMsg(challenge) @@ -270,7 +270,7 @@ func (ac *awsConversation) getCredentials() (*awsv4.StaticProvider, error) { return creds, err } -func (ac *awsConversation) firstMsg() ([]byte, error) { +func (ac *awsConversation) firstMsg() []byte { // Values are cached for use in final message parameters ac.nonce = make([]byte, 32) _, _ = rand.Read(ac.nonce) @@ -279,7 +279,7 @@ func (ac *awsConversation) firstMsg() ([]byte, error) { msg = bsoncore.AppendInt32Element(msg, "p", 110) msg = bsoncore.AppendBinaryElement(msg, "r", 0x00, ac.nonce) msg, _ = bsoncore.AppendDocumentEnd(msg, idx) - return msg, nil + return msg } func (ac *awsConversation) finalMsg(s1 []byte) ([]byte, error) { diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/conversation.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/conversation.go index 2d76e29e..fe8f472c 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/conversation.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/conversation.go @@ -16,7 +16,7 @@ import ( // handshake. // // FirstMessage method returns the first message to be sent to the server. This message will be included in the initial -// isMaster command. +// hello command. // // Finish takes the server response to the initial message and conducts the remainder of the conversation to // authenticate the provided connection. diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/default.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/default.go index 4da4032f..e266ad54 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/default.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/default.go @@ -36,7 +36,7 @@ type DefaultAuthenticator struct { Cred *Cred // The authenticator to use for speculative authentication. Because the correct auth mechanism is unknown when doing - // the initial isMaster, SCRAM-SHA-256 is used for the speculative attempt. + // the initial hello, SCRAM-SHA-256 is used for the speculative attempt. speculativeAuthenticator SpeculativeAuthenticator } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/gssapi.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/gssapi.go index 6e9f398d..4b860ba6 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/gssapi.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/gssapi.go @@ -4,8 +4,9 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -//+build gssapi -//+build windows linux darwin +//go:build gssapi && (windows || linux || darwin) +// +build gssapi +// +build windows linux darwin package auth diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/gssapi_not_enabled.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/gssapi_not_enabled.go index d88b764f..50522cbb 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/gssapi_not_enabled.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/gssapi_not_enabled.go @@ -4,7 +4,8 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -//+build !gssapi +//go:build !gssapi +// +build !gssapi package auth diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/gssapi_not_supported.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/gssapi_not_supported.go index 55caa284..10312c22 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/gssapi_not_supported.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/gssapi_not_supported.go @@ -4,7 +4,8 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -//+build gssapi,!windows,!linux,!darwin +//go:build gssapi && !windows && !linux && !darwin +// +build gssapi,!windows,!linux,!darwin package auth diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/awsv4/rules.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/awsv4/rules.go index 2649db45..ad820d8e 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/awsv4/rules.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/awsv4/rules.go @@ -45,24 +45,24 @@ func (m mapRule) IsValid(value string) bool { return ok } -// whitelist is a generic rule for whitelisting -type whitelist struct { +// allowlist is a generic rule for allowlisting +type allowlist struct { rule } -// IsValid for whitelist checks if the value is within the whitelist -func (w whitelist) IsValid(value string) bool { - return w.rule.IsValid(value) +// IsValid for allowlist checks if the value is within the allowlist +func (a allowlist) IsValid(value string) bool { + return a.rule.IsValid(value) } -// blacklist is a generic rule for blacklisting -type blacklist struct { +// denylist is a generic rule for denylisting +type denylist struct { rule } -// IsValid for whitelist checks if the value is within the whitelist -func (b blacklist) IsValid(value string) bool { - return !b.rule.IsValid(value) +// IsValid for allowlist checks if the value is within the allowlist +func (d denylist) IsValid(value string) bool { + return !d.rule.IsValid(value) } type patterns []string diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/awsv4/signer.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/awsv4/signer.go index 1e4662b0..23508c1f 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/awsv4/signer.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/awsv4/signer.go @@ -41,7 +41,7 @@ const ( ) var ignoredHeaders = rules{ - blacklist{ + denylist{ mapRule{ authorizationHeader: struct{}{}, "User-Agent": struct{}{}, @@ -234,7 +234,7 @@ func (ctx *signingCtx) buildCredentialString() { } func (ctx *signingCtx) buildCanonicalHeaders(r rule, header http.Header) { - var headers []string + headers := make([]string, 0, len(header)) headers = append(headers, "host") for k, v := range header { if !r.IsValid(k) { diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/gssapi/gss.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/gssapi/gss.go index 44faae4e..abfa4db4 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/gssapi/gss.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/gssapi/gss.go @@ -4,8 +4,9 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -//+build gssapi -//+build linux darwin +//go:build gssapi && (linux || darwin) +// +build gssapi +// +build linux darwin package gssapi diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/gssapi/sspi.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/gssapi/sspi.go index f3c3c75c..36e9633f 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/gssapi/sspi.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/gssapi/sspi.go @@ -4,7 +4,8 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -//+build gssapi,windows +//go:build gssapi && windows +// +build gssapi,windows package gssapi diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/mongodbaws.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/mongodbaws.go index c485aa12..8b81865e 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/mongodbaws.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/mongodbaws.go @@ -60,7 +60,7 @@ func (a *awsSaslAdapter) Start() (string, []byte, error) { if err != nil { return MongoDBAWS, nil, err } - return MongoDBAWS, []byte(step), nil + return MongoDBAWS, step, nil } func (a *awsSaslAdapter) Next(challenge []byte) ([]byte, error) { diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/mongodbcr.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/mongodbcr.go index d39772ef..6e2c2f4d 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/mongodbcr.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/mongodbcr.go @@ -8,11 +8,14 @@ package auth import ( "context" - "crypto/md5" "fmt" - "io" + // Ignore gosec warning "Blocklisted import crypto/md5: weak cryptographic primitive". We need + // to use MD5 here to implement the MONGODB-CR specification. + /* #nosec G501 */ + "crypto/md5" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" "go.mongodb.org/mongo-driver/x/mongo/driver" @@ -21,7 +24,8 @@ import ( // MONGODBCR is the mechanism name for MONGODB-CR. // -// The MONGODB-CR authentication mechanism is deprecated in MongoDB 4.0. +// The MONGODB-CR authentication mechanism is deprecated in MongoDB 3.6 and removed in +// MongoDB 4.0. const MONGODBCR = "MONGODB-CR" func newMongoDBCRAuthenticator(cred *Cred) (Authenticator, error) { @@ -34,7 +38,8 @@ func newMongoDBCRAuthenticator(cred *Cred) (Authenticator, error) { // MongoDBCRAuthenticator uses the MONGODB-CR algorithm to authenticate a connection. // -// The MONGODB-CR authentication mechanism is deprecated in MongoDB 4.0. +// The MONGODB-CR authentication mechanism is deprecated in MongoDB 3.6 and removed in +// MongoDB 4.0. type MongoDBCRAuthenticator struct { DB string Username string @@ -43,7 +48,8 @@ type MongoDBCRAuthenticator struct { // Auth authenticates the connection. // -// The MONGODB-CR authentication mechanism is deprecated in MongoDB 4.0. +// The MONGODB-CR authentication mechanism is deprecated in MongoDB 3.6 and removed in +// MongoDB 4.0. func (a *MongoDBCRAuthenticator) Auth(ctx context.Context, cfg *Config) error { db := a.DB @@ -92,6 +98,9 @@ func (a *MongoDBCRAuthenticator) Auth(ctx context.Context, cfg *Config) error { } func (a *MongoDBCRAuthenticator) createKey(nonce string) string { + // Ignore gosec warning "Use of weak cryptographic primitive". We need to use MD5 here to + // implement the MONGODB-CR specification. + /* #nosec G401 */ h := md5.New() _, _ = io.WriteString(h, nonce) diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/sasl.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/sasl.go index b91a08f1..a7ae3368 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/sasl.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/sasl.go @@ -73,7 +73,7 @@ func (sc *saslConversation) FirstMessage() (bsoncore.Document, error) { bsoncore.AppendBinaryElement(nil, "payload", 0x00, payload), } if sc.speculative { - // The "db" field is only appended for speculative auth because the isMaster command is executed against admin + // The "db" field is only appended for speculative auth because the hello command is executed against admin // so this is needed to tell the server the user's auth source. For a non-speculative attempt, the SASL commands // will be executed against the auth source. saslCmdElements = append(saslCmdElements, bsoncore.AppendStringElement(nil, "db", sc.source)) diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/util.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/util.go index 36b8c074..a75a006d 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/util.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/auth/util.go @@ -7,14 +7,21 @@ package auth import ( - "crypto/md5" "fmt" "io" + + // Ignore gosec warning "Blocklisted import crypto/md5: weak cryptographic primitive". We need + // to use MD5 here to implement the SCRAM specification. + /* #nosec G501 */ + "crypto/md5" ) const defaultAuthDB = "admin" func mongoPasswordDigest(username, password string) string { + // Ignore gosec warning "Use of weak cryptographic primitive". We need to use MD5 here to + // implement the SCRAM specification. + /* #nosec G401 */ h := md5.New() _, _ = io.WriteString(h, username) _, _ = io.WriteString(h, ":mongo:") diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/batch_cursor.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/batch_cursor.go index 4c3e4acf..a25e35d3 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/batch_cursor.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/batch_cursor.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "strings" + "time" "go.mongodb.org/mongo-driver/bson/bsontype" "go.mongodb.org/mongo-driver/event" @@ -31,7 +32,7 @@ type BatchCursor struct { firstBatch bool cmdMonitor *event.CommandMonitor postBatchResumeToken bsoncore.Document - crypt *Crypt + crypt Crypt serverAPI *ServerAPIOptions // legacy server (< 3.2) fields @@ -129,7 +130,7 @@ type CursorOptions struct { MaxTimeMS int64 Limit int32 CommandMonitor *event.CommandMonitor - Crypt *Crypt + Crypt Crypt ServerAPI *ServerAPIOptions } @@ -183,6 +184,21 @@ func NewEmptyBatchCursor() *BatchCursor { return &BatchCursor{currentBatch: new(bsoncore.DocumentSequence)} } +// NewBatchCursorFromDocuments returns a batch cursor with current batch set to a sequence-style +// DocumentSequence containing the provided documents. +func NewBatchCursorFromDocuments(documents []byte) *BatchCursor { + return &BatchCursor{ + currentBatch: &bsoncore.DocumentSequence{ + Data: documents, + Style: bsoncore.SequenceStyle, + }, + // BatchCursors created with this function have no associated ID nor server, so no getMore + // calls will be made. + id: 0, + server: nil, + } +} + // ID returns the cursor ID for this batch cursor. func (bc *BatchCursor) ID() int64 { return bc.id @@ -345,7 +361,7 @@ func (bc *BatchCursor) getMore(ctx context.Context) { return nil } - bc.postBatchResumeToken = bsoncore.Document(pbrtDoc) + bc.postBatchResumeToken = pbrtDoc return nil }, @@ -380,7 +396,6 @@ func (bc *BatchCursor) getMore(ctx context.Context) { bc.err = err } } - return } // PostBatchResumeToken returns the latest seen post batch resume token. @@ -427,6 +442,11 @@ func (lbcd *loadBalancedCursorDeployment) Connection(_ context.Context) (Connect return lbcd.conn, nil } +// MinRTT always returns 0. It implements the driver.Server interface. +func (lbcd *loadBalancedCursorDeployment) MinRTT() time.Duration { + return 0 +} + func (lbcd *loadBalancedCursorDeployment) ProcessError(err error, conn Connection) ProcessErrorResult { return lbcd.errorProcessor.ProcessError(err, conn) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/batches.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/batches.go index f376b6d3..4c25abd6 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/batches.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/batches.go @@ -6,10 +6,6 @@ import ( "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" ) -// this is the amount of reserved buffer space in a message that the -// driver reserves for command overhead. -const reservedCommandBufferBytes = 16 * 10 * 10 * 10 - // ErrDocumentTooLarge occurs when a document that is larger than the maximum size accepted by a // server is passed to an insert command. var ErrDocumentTooLarge = errors.New("an inserted document is too large") diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/connstring/connstring.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/connstring/connstring.go index 2e20a055..57ce349b 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/connstring/connstring.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/connstring/connstring.go @@ -9,6 +9,7 @@ package connstring // import "go.mongodb.org/mongo-driver/x/mongo/driver/connstr import ( "errors" "fmt" + "math/rand" "net" "net/url" "strconv" @@ -16,11 +17,15 @@ import ( "time" "go.mongodb.org/mongo-driver/internal" + "go.mongodb.org/mongo-driver/internal/randutil" "go.mongodb.org/mongo-driver/mongo/writeconcern" "go.mongodb.org/mongo-driver/x/mongo/driver/dns" "go.mongodb.org/mongo-driver/x/mongo/driver/wiremessage" ) +// random is a package-global pseudo-random number generator. +var random = randutil.NewLockedRand(rand.NewSource(randutil.CryptoSeed())) + // ParseAndValidate parses the provided URI into a ConnString object. // It check that all values are valid. func ParseAndValidate(s string) (ConnString, error) { @@ -80,6 +85,8 @@ type ConnString struct { MaxPoolSizeSet bool MinPoolSize uint64 MinPoolSizeSet bool + MaxConnecting uint64 + MaxConnectingSet bool Password string PasswordSet bool ReadConcernLevel string @@ -97,6 +104,8 @@ type ConnString struct { ServerSelectionTimeoutSet bool SocketTimeout time.Duration SocketTimeoutSet bool + SRVMaxHosts int + SRVServiceName string SSL bool SSLSet bool SSLClientCertificateKeyFile string @@ -254,14 +263,25 @@ func (p *parser) parse(original string) error { hosts = uri[:idx] } - var connectionArgsFromTXT []string parsedHosts := strings.Split(hosts, ",") + uri = uri[len(hosts):] + extractedDatabase, err := extractDatabaseFromURI(uri) + if err != nil { + return err + } + + uri = extractedDatabase.uri + p.Database = extractedDatabase.db + // grab connection arguments from URI + connectionArgsFromQueryString, err := extractQueryArgsFromURI(uri) + if err != nil { + return err + } + + // grab connection arguments from TXT record and enable SSL if "mongodb+srv://" + var connectionArgsFromTXT []string if p.Scheme == SchemeMongoDBSRV { - parsedHosts, err = p.dnsResolver.ParseHosts(hosts, true) - if err != nil { - return err - } connectionArgsFromTXT, err = p.dnsResolver.GetConnectionArgsFromTXT(hosts) if err != nil { return err @@ -272,35 +292,41 @@ func (p *parser) parse(original string) error { p.SSLSet = true } - for _, host := range parsedHosts { - err = p.addHost(host) + // add connection arguments from URI and TXT records to connstring + connectionArgPairs := append(connectionArgsFromTXT, connectionArgsFromQueryString...) + for _, pair := range connectionArgPairs { + err := p.addOption(pair) if err != nil { - return internal.WrapErrorf(err, "invalid host \"%s\"", host) + return err } } - if len(p.Hosts) == 0 { - return fmt.Errorf("must have at least 1 host") - } - uri = uri[len(hosts):] + // do SRV lookup if "mongodb+srv://" + if p.Scheme == SchemeMongoDBSRV { + parsedHosts, err = p.dnsResolver.ParseHosts(hosts, p.SRVServiceName, true) + if err != nil { + return err + } - extractedDatabase, err := extractDatabaseFromURI(uri) - if err != nil { - return err + // If p.SRVMaxHosts is non-zero and is less than the number of hosts, randomly + // select SRVMaxHosts hosts from parsedHosts. + if p.SRVMaxHosts > 0 && p.SRVMaxHosts < len(parsedHosts) { + random.Shuffle(len(parsedHosts), func(i, j int) { + parsedHosts[i], parsedHosts[j] = parsedHosts[j], parsedHosts[i] + }) + parsedHosts = parsedHosts[:p.SRVMaxHosts] + } } - uri = extractedDatabase.uri - p.Database = extractedDatabase.db - - connectionArgsFromQueryString, err := extractQueryArgsFromURI(uri) - connectionArgPairs := append(connectionArgsFromTXT, connectionArgsFromQueryString...) - - for _, pair := range connectionArgPairs { - err = p.addOption(pair) + for _, host := range parsedHosts { + err = p.addHost(host) if err != nil { - return err + return internal.WrapErrorf(err, "invalid host \"%s\"", host) } } + if len(p.Hosts) == 0 { + return fmt.Errorf("must have at least 1 host") + } err = p.setDefaultAuthParams(extractedDatabase.db) if err != nil { @@ -355,6 +381,16 @@ func (p *parser) validate() error { } } + // Check for invalid use of SRVMaxHosts. + if p.SRVMaxHosts > 0 { + if p.ReplicaSet != "" { + return internal.ErrSRVMaxHostsWithReplicaSet + } + if p.LoadBalanced { + return internal.ErrSRVMaxHostsWithLoadBalanced + } + } + return nil } @@ -697,13 +733,22 @@ func (p *parser) addOption(pair string) error { } p.MinPoolSize = uint64(n) p.MinPoolSizeSet = true + case "maxconnecting": + n, err := strconv.Atoi(value) + if err != nil || n < 0 { + return fmt.Errorf("invalid value for %s: %s", key, value) + } + p.MaxConnecting = uint64(n) + p.MaxConnectingSet = true case "readconcernlevel": p.ReadConcernLevel = value case "readpreference": p.ReadPreference = value case "readpreferencetags": if value == "" { - // for when readPreferenceTags= at end of URI + // If "readPreferenceTags=" is supplied, append an empty map to tag sets to + // represent a wild-card. + p.ReadPreferenceTagSets = append(p.ReadPreferenceTagSets, map[string]string{}) break } @@ -762,6 +807,31 @@ func (p *parser) addOption(pair string) error { } p.SocketTimeout = time.Duration(n) * time.Millisecond p.SocketTimeoutSet = true + case "srvmaxhosts": + // srvMaxHosts can only be set on URIs with the "mongodb+srv" scheme + if p.Scheme != SchemeMongoDBSRV { + return fmt.Errorf("cannot specify srvMaxHosts on non-SRV URI") + } + + n, err := strconv.Atoi(value) + if err != nil || n < 0 { + return fmt.Errorf("invalid value for %s: %s", key, value) + } + p.SRVMaxHosts = n + case "srvservicename": + // srvServiceName can only be set on URIs with the "mongodb+srv" scheme + if p.Scheme != SchemeMongoDBSRV { + return fmt.Errorf("cannot specify srvServiceName on non-SRV URI") + } + + // srvServiceName must be between 1 and 62 characters according to + // our specification. Empty service names are not valid, and the service + // name (including prepended underscore) should not exceed the 63 character + // limit for DNS query subdomains. + if len(value) < 1 || len(value) > 62 { + return fmt.Errorf("srvServiceName value must be between 1 and 62 characters") + } + p.SRVServiceName = value case "ssl", "tls": switch value { case "true": diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/crypt.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/crypt.go index 812db32f..b06e0581 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/crypt.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/crypt.go @@ -40,27 +40,52 @@ type CryptOptions struct { MarkFn MarkCommandFn KmsProviders bsoncore.Document SchemaMap map[string]bsoncore.Document + TLSConfig map[string]*tls.Config BypassAutoEncryption bool } -// Crypt consumes the libmongocrypt.MongoCrypt type to iterate the mongocrypt state machine and perform encryption +// Crypt is an interface implemented by types that can encrypt and decrypt instances of +// bsoncore.Document. +// +// Users should rely on the driver's crypt type (used by default) for encryption and decryption +// unless they are perfectly confident in another implementation of Crypt. +type Crypt interface { + // Encrypt encrypts the given command. + Encrypt(ctx context.Context, db string, cmd bsoncore.Document) (bsoncore.Document, error) + // Decrypt decrypts the given command response. + Decrypt(ctx context.Context, cmdResponse bsoncore.Document) (bsoncore.Document, error) + // CreateDataKey creates a data key using the given KMS provider and options. + CreateDataKey(ctx context.Context, kmsProvider string, opts *options.DataKeyOptions) (bsoncore.Document, error) + // EncryptExplicit encrypts the given value with the given options. + EncryptExplicit(ctx context.Context, val bsoncore.Value, opts *options.ExplicitEncryptionOptions) (byte, []byte, error) + // DecryptExplicit decrypts the given encrypted value. + DecryptExplicit(ctx context.Context, subtype byte, data []byte) (bsoncore.Value, error) + // Close cleans up any resources associated with the Crypt instance. + Close() + // BypassAutoEncryption returns true if auto-encryption should be bypassed. + BypassAutoEncryption() bool +} + +// crypt consumes the libmongocrypt.MongoCrypt type to iterate the mongocrypt state machine and perform encryption // and decryption. -type Crypt struct { +type crypt struct { mongoCrypt *mongocrypt.MongoCrypt collInfoFn CollectionInfoFn keyFn KeyRetrieverFn markFn MarkCommandFn + tlsConfig map[string]*tls.Config - BypassAutoEncryption bool + bypassAutoEncryption bool } // NewCrypt creates a new Crypt instance configured with the given AutoEncryptionOptions. -func NewCrypt(opts *CryptOptions) (*Crypt, error) { - c := &Crypt{ +func NewCrypt(opts *CryptOptions) (Crypt, error) { + c := &crypt{ collInfoFn: opts.CollInfoFn, keyFn: opts.KeyFn, markFn: opts.MarkFn, - BypassAutoEncryption: opts.BypassAutoEncryption, + tlsConfig: opts.TLSConfig, + bypassAutoEncryption: opts.BypassAutoEncryption, } mongocryptOpts := options.MongoCrypt().SetKmsProviders(opts.KmsProviders).SetLocalSchemaMap(opts.SchemaMap) @@ -74,8 +99,8 @@ func NewCrypt(opts *CryptOptions) (*Crypt, error) { } // Encrypt encrypts the given command. -func (c *Crypt) Encrypt(ctx context.Context, db string, cmd bsoncore.Document) (bsoncore.Document, error) { - if c.BypassAutoEncryption { +func (c *crypt) Encrypt(ctx context.Context, db string, cmd bsoncore.Document) (bsoncore.Document, error) { + if c.bypassAutoEncryption { return cmd, nil } @@ -89,7 +114,7 @@ func (c *Crypt) Encrypt(ctx context.Context, db string, cmd bsoncore.Document) ( } // Decrypt decrypts the given command response. -func (c *Crypt) Decrypt(ctx context.Context, cmdResponse bsoncore.Document) (bsoncore.Document, error) { +func (c *crypt) Decrypt(ctx context.Context, cmdResponse bsoncore.Document) (bsoncore.Document, error) { cryptCtx, err := c.mongoCrypt.CreateDecryptionContext(cmdResponse) if err != nil { return nil, err @@ -100,7 +125,7 @@ func (c *Crypt) Decrypt(ctx context.Context, cmdResponse bsoncore.Document) (bso } // CreateDataKey creates a data key using the given KMS provider and options. -func (c *Crypt) CreateDataKey(ctx context.Context, kmsProvider string, opts *options.DataKeyOptions) (bsoncore.Document, error) { +func (c *crypt) CreateDataKey(ctx context.Context, kmsProvider string, opts *options.DataKeyOptions) (bsoncore.Document, error) { cryptCtx, err := c.mongoCrypt.CreateDataKeyContext(kmsProvider, opts) if err != nil { return nil, err @@ -111,7 +136,7 @@ func (c *Crypt) CreateDataKey(ctx context.Context, kmsProvider string, opts *opt } // EncryptExplicit encrypts the given value with the given options. -func (c *Crypt) EncryptExplicit(ctx context.Context, val bsoncore.Value, opts *options.ExplicitEncryptionOptions) (byte, []byte, error) { +func (c *crypt) EncryptExplicit(ctx context.Context, val bsoncore.Value, opts *options.ExplicitEncryptionOptions) (byte, []byte, error) { idx, doc := bsoncore.AppendDocumentStart(nil) doc = bsoncore.AppendValueElement(doc, "v", val) doc, _ = bsoncore.AppendDocumentEnd(doc, idx) @@ -132,7 +157,7 @@ func (c *Crypt) EncryptExplicit(ctx context.Context, val bsoncore.Value, opts *o } // DecryptExplicit decrypts the given encrypted value. -func (c *Crypt) DecryptExplicit(ctx context.Context, subtype byte, data []byte) (bsoncore.Value, error) { +func (c *crypt) DecryptExplicit(ctx context.Context, subtype byte, data []byte) (bsoncore.Value, error) { idx, doc := bsoncore.AppendDocumentStart(nil) doc = bsoncore.AppendBinaryElement(doc, "v", subtype, data) doc, _ = bsoncore.AppendDocumentEnd(doc, idx) @@ -152,11 +177,15 @@ func (c *Crypt) DecryptExplicit(ctx context.Context, subtype byte, data []byte) } // Close cleans up any resources associated with the Crypt instance. -func (c *Crypt) Close() { +func (c *crypt) Close() { c.mongoCrypt.Close() } -func (c *Crypt) executeStateMachine(ctx context.Context, cryptCtx *mongocrypt.Context, db string) (bsoncore.Document, error) { +func (c *crypt) BypassAutoEncryption() bool { + return c.bypassAutoEncryption +} + +func (c *crypt) executeStateMachine(ctx context.Context, cryptCtx *mongocrypt.Context, db string) (bsoncore.Document, error) { var err error for { state := cryptCtx.State() @@ -168,7 +197,7 @@ func (c *Crypt) executeStateMachine(ctx context.Context, cryptCtx *mongocrypt.Co case mongocrypt.NeedMongoKeys: err = c.retrieveKeys(ctx, cryptCtx) case mongocrypt.NeedKms: - err = c.decryptKeys(ctx, cryptCtx) + err = c.decryptKeys(cryptCtx) case mongocrypt.Ready: return cryptCtx.Finish() default: @@ -180,7 +209,7 @@ func (c *Crypt) executeStateMachine(ctx context.Context, cryptCtx *mongocrypt.Co } } -func (c *Crypt) collectionInfo(ctx context.Context, cryptCtx *mongocrypt.Context, db string) error { +func (c *crypt) collectionInfo(ctx context.Context, cryptCtx *mongocrypt.Context, db string) error { op, err := cryptCtx.NextOperation() if err != nil { return err @@ -199,7 +228,7 @@ func (c *Crypt) collectionInfo(ctx context.Context, cryptCtx *mongocrypt.Context return cryptCtx.CompleteOperation() } -func (c *Crypt) markCommand(ctx context.Context, cryptCtx *mongocrypt.Context, db string) error { +func (c *crypt) markCommand(ctx context.Context, cryptCtx *mongocrypt.Context, db string) error { op, err := cryptCtx.NextOperation() if err != nil { return err @@ -216,7 +245,7 @@ func (c *Crypt) markCommand(ctx context.Context, cryptCtx *mongocrypt.Context, d return cryptCtx.CompleteOperation() } -func (c *Crypt) retrieveKeys(ctx context.Context, cryptCtx *mongocrypt.Context) error { +func (c *crypt) retrieveKeys(ctx context.Context, cryptCtx *mongocrypt.Context) error { op, err := cryptCtx.NextOperation() if err != nil { return err @@ -236,14 +265,14 @@ func (c *Crypt) retrieveKeys(ctx context.Context, cryptCtx *mongocrypt.Context) return cryptCtx.CompleteOperation() } -func (c *Crypt) decryptKeys(ctx context.Context, cryptCtx *mongocrypt.Context) error { +func (c *crypt) decryptKeys(cryptCtx *mongocrypt.Context) error { for { kmsCtx := cryptCtx.NextKmsContext() if kmsCtx == nil { break } - if err := c.decryptKey(ctx, kmsCtx); err != nil { + if err := c.decryptKey(kmsCtx); err != nil { return err } } @@ -251,7 +280,7 @@ func (c *Crypt) decryptKeys(ctx context.Context, cryptCtx *mongocrypt.Context) e return cryptCtx.FinishKmsContexts() } -func (c *Crypt) decryptKey(ctx context.Context, kmsCtx *mongocrypt.KmsContext) error { +func (c *crypt) decryptKey(kmsCtx *mongocrypt.KmsContext) error { host, err := kmsCtx.HostName() if err != nil { return err @@ -267,7 +296,12 @@ func (c *Crypt) decryptKey(ctx context.Context, kmsCtx *mongocrypt.KmsContext) e addr = fmt.Sprintf("%s:%d", host, defaultKmsPort) } - conn, err := tls.Dial("tcp", addr, &tls.Config{}) + kmsProvider := kmsCtx.KMSProvider() + tlsCfg := c.tlsConfig[kmsProvider] + if tlsCfg == nil { + tlsCfg = &tls.Config{MinVersion: tls.VersionTLS12} + } + conn, err := tls.Dial("tcp", addr, tlsCfg) if err != nil { return err } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/dns/dns.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/dns/dns.go index f71337d1..16268b59 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/dns/dns.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/dns/dns.go @@ -24,14 +24,14 @@ type Resolver struct { // DefaultResolver is a Resolver that uses the default Resolver from the net package. var DefaultResolver = &Resolver{net.LookupSRV, net.LookupTXT} -// ParseHosts uses the srv string to get the hosts. -func (r *Resolver) ParseHosts(host string, stopOnErr bool) ([]string, error) { +// ParseHosts uses the srv string and service name to get the hosts. +func (r *Resolver) ParseHosts(host string, srvName string, stopOnErr bool) ([]string, error) { parsedHosts := strings.Split(host, ",") if len(parsedHosts) != 1 { return nil, fmt.Errorf("URI with SRV must include one and only one hostname") } - return r.fetchSeedlistFromSRV(parsedHosts[0], stopOnErr) + return r.fetchSeedlistFromSRV(parsedHosts[0], srvName, stopOnErr) } // GetConnectionArgsFromTXT gets the TXT record associated with the host and returns the connection arguments. @@ -64,7 +64,7 @@ func (r *Resolver) GetConnectionArgsFromTXT(host string) ([]string, error) { return connectionArgsFromTXT, nil } -func (r *Resolver) fetchSeedlistFromSRV(host string, stopOnErr bool) ([]string, error) { +func (r *Resolver) fetchSeedlistFromSRV(host string, srvName string, stopOnErr bool) ([]string, error) { var err error _, _, err = net.SplitHostPort(host) @@ -75,14 +75,18 @@ func (r *Resolver) fetchSeedlistFromSRV(host string, stopOnErr bool) ([]string, return nil, fmt.Errorf("URI with srv must not include a port number") } - _, addresses, err := r.LookupSRV("mongodb", "tcp", host) + // default to "mongodb" as service name if not supplied + if srvName == "" { + srvName = "mongodb" + } + _, addresses, err := r.LookupSRV(srvName, "tcp", host) if err != nil { return nil, err } trimmedHost := strings.TrimSuffix(host, ".") - var parsedHosts []string + parsedHosts := make([]string, 0, len(addresses)) for _, address := range addresses { trimmedAddressTarget := strings.TrimSuffix(address.Target, ".") err := validateSRVResult(trimmedAddressTarget, trimmedHost) diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/driver.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/driver.go index 6ba7c770..9a2ccda7 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/driver.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/driver.go @@ -2,6 +2,7 @@ package driver // import "go.mongodb.org/mongo-driver/x/mongo/driver" import ( "context" + "time" "go.mongodb.org/mongo-driver/mongo/address" "go.mongodb.org/mongo-driver/mongo/description" @@ -43,6 +44,9 @@ type Subscriber interface { // retrieving and returning of connections. type Server interface { Connection(context.Context) (Connection, error) + + // MinRTT returns the minimum round-trip time to the server observed over the window period. + MinRTT() time.Duration } // Connection represents a connection to a MongoDB server. @@ -50,8 +54,14 @@ type Connection interface { WriteWireMessage(context.Context, []byte) error ReadWireMessage(ctx context.Context, dst []byte) ([]byte, error) Description() description.Server + + // Close closes any underlying connection and returns or frees any resources held by the + // connection. Close is idempotent and can be called multiple times, although subsequent calls + // to Close may return an error. A connection cannot be used after it is closed. Close() error + ID() string + ServerConnectionID() *int32 Address() address.Address Stale() bool } @@ -137,11 +147,14 @@ type ErrorProcessor interface { } // HandshakeInformation contains information extracted from a MongoDB connection handshake. This is a helper type that -// augments description.Server by also tracking authentication-related fields. We use this type rather than adding -// these fields to description.Server to avoid retaining sensitive information in a user-facing type. +// augments description.Server by also tracking server connection ID and authentication-related fields. We use this type +// rather than adding authentication-related fields to description.Server to avoid retaining sensitive information in a +// user-facing type. The server connection ID is stored in this type because unlike description.Server, all handshakes are +// correlated with a single network connection. type HandshakeInformation struct { Description description.Server SpeculativeAuthenticate bsoncore.Document + ServerConnectionID *int32 SaslSupportedMechs []string } @@ -190,6 +203,11 @@ func (ssd SingleConnectionDeployment) Connection(context.Context) (Connection, e return ssd.C, nil } +// MinRTT always returns 0. It implements the driver.Server interface. +func (ssd SingleConnectionDeployment) MinRTT() time.Duration { + return 0 +} + // TODO(GODRIVER-617): We can likely use 1 type for both the Type and the RetryMode by using // 2 bits for the mode and 1 bit for the type. Although in the practical sense, we might not want to // do that since the type of retryability is tied to the operation itself and isn't going change, diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/errors.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/errors.go index 23c94a8b..fd1e2ad1 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/errors.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/errors.go @@ -7,6 +7,7 @@ import ( "strings" "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/internal" "go.mongodb.org/mongo-driver/mongo/description" "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" ) @@ -14,7 +15,7 @@ import ( var ( retryableCodes = []int32{11600, 11602, 10107, 13435, 13436, 189, 91, 7, 6, 89, 9001, 262} nodeIsRecoveringCodes = []int32{11600, 11602, 13436, 189, 91} - notMasterCodes = []int32{10107, 13435, 10058} + notPrimaryCodes = []int32{10107, 13435, 10058} nodeIsShuttingDownCodes = []int32{11600, 91} unknownReplWriteConcernCode = int32(79) @@ -73,7 +74,7 @@ func (e ResponseError) Error() string { if e.Wrapped != nil { return fmt.Sprintf("%s: %s", e.Message, e.Wrapped) } - return fmt.Sprintf("%s", e.Message) + return e.Message } // WriteCommandError is an error for a write command. @@ -81,6 +82,7 @@ type WriteCommandError struct { WriteConcernError *WriteConcernError WriteErrors WriteErrors Labels []string + Raw bsoncore.Document } // UnsupportedStorageEngine returns whether or not the WriteCommandError comes from a retryable write being attempted @@ -128,6 +130,7 @@ type WriteConcernError struct { Details bsoncore.Document Labels []string TopologyVersion *description.TopologyVersion + Raw bsoncore.Document } func (wce WriteConcernError) Error() string { @@ -170,15 +173,15 @@ func (wce WriteConcernError) NodeIsShuttingDown() bool { return hasNoCode && strings.Contains(wce.Message, "node is shutting down") } -// NotMaster returns true if this error is a not master error. -func (wce WriteConcernError) NotMaster() bool { - for _, code := range notMasterCodes { +// NotPrimary returns true if this error is a not primary error. +func (wce WriteConcernError) NotPrimary() bool { + for _, code := range notPrimaryCodes { if wce.Code == int64(code) { return true } } hasNoCode := wce.Code == 0 - return hasNoCode && strings.Contains(wce.Message, "not master") + return hasNoCode && strings.Contains(wce.Message, internal.LegacyNotPrimary) } // WriteError is a non-write concern failure that occurred as a result of a write @@ -188,6 +191,7 @@ type WriteError struct { Code int64 Message string Details bsoncore.Document + Raw bsoncore.Document } func (we WriteError) Error() string { return we.Message } @@ -217,6 +221,7 @@ type Error struct { Name string Wrapped error TopologyVersion *description.TopologyVersion + Raw bsoncore.Document } // UnsupportedStorageEngine returns whether e came as a result of an unsupported storage engine @@ -316,15 +321,15 @@ func (e Error) NodeIsShuttingDown() bool { return hasNoCode && strings.Contains(e.Message, "node is shutting down") } -// NotMaster returns true if this error is a not master error. -func (e Error) NotMaster() bool { - for _, code := range notMasterCodes { +// NotPrimary returns true if this error is a not primary error. +func (e Error) NotPrimary() bool { + for _, code := range notPrimaryCodes { if e.Code == code { return true } } hasNoCode := e.Code == 0 - return hasNoCode && strings.Contains(e.Message, "not master") + return hasNoCode && strings.Contains(e.Message, internal.LegacyNotPrimary) } // NamespaceNotFound returns true if this errors is a NamespaceNotFound error. @@ -416,6 +421,7 @@ func ExtractErrorFromServerResponse(doc bsoncore.Document) error { we.Details = make([]byte, len(info)) copy(we.Details, info) } + we.Raw = doc wcError.WriteErrors = append(wcError.WriteErrors, we) } case "writeConcernError": @@ -424,6 +430,7 @@ func ExtractErrorFromServerResponse(doc bsoncore.Document) error { break } wcError.WriteConcernError = new(WriteConcernError) + wcError.WriteConcernError.Raw = doc if code, exists := doc.Lookup("code").AsInt64OK(); exists { wcError.WriteConcernError.Code = code } @@ -471,6 +478,7 @@ func ExtractErrorFromServerResponse(doc bsoncore.Document) error { Name: codeName, Labels: labels, TopologyVersion: tv, + Raw: doc, } } @@ -479,6 +487,7 @@ func ExtractErrorFromServerResponse(doc bsoncore.Document) error { if wcError.WriteConcernError != nil { wcError.WriteConcernError.TopologyVersion = tv } + wcError.Raw = doc return wcError } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/binary.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/binary.go index 3794ee5c..9e887375 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/binary.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/binary.go @@ -4,6 +4,7 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +//go:build cse // +build cse package mongocrypt diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/errors.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/errors.go index 8c235c5b..3401e738 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/errors.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/errors.go @@ -4,6 +4,7 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +//go:build cse // +build cse package mongocrypt diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/errors_not_enabled.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/errors_not_enabled.go index 816099ab..706a0f9e 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/errors_not_enabled.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/errors_not_enabled.go @@ -4,6 +4,7 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +//go:build !cse // +build !cse package mongocrypt diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt.go index edfedf1a..ba547ad4 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt.go @@ -4,6 +4,7 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +//go:build cse // +build cse package mongocrypt diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_context.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_context.go index dc0dd514..a5380196 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_context.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_context.go @@ -4,6 +4,7 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +//go:build cse // +build cse package mongocrypt diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_context_not_enabled.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_context_not_enabled.go index b0ff1689..624888a2 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_context_not_enabled.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_context_not_enabled.go @@ -4,6 +4,7 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +//go:build !cse // +build !cse package mongocrypt diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_kms_context.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_kms_context.go index 77ce8308..296a2231 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_kms_context.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_kms_context.go @@ -4,6 +4,7 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +//go:build cse // +build cse package mongocrypt @@ -32,6 +33,12 @@ func (kc *KmsContext) HostName() (string, error) { return C.GoString(hostname), nil } +// KMSProvider gets the KMS provider of the KMS context. +func (kc *KmsContext) KMSProvider() string { + kmsProvider := C.mongocrypt_kms_ctx_get_kms_provider(kc.wrapped, nil) + return C.GoString(kmsProvider) +} + // Message returns the message to send to the KMS. func (kc *KmsContext) Message() ([]byte, error) { msgBinary := newBinary() diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_kms_context_not_enabled.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_kms_context_not_enabled.go index 8026a190..272367ea 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_kms_context_not_enabled.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_kms_context_not_enabled.go @@ -4,6 +4,7 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +//go:build !cse // +build !cse package mongocrypt @@ -21,6 +22,11 @@ func (kc *KmsContext) Message() ([]byte, error) { panic(cseNotSupportedMsg) } +// KMSProvider gets the KMS provider of the KMS context. +func (kc *KmsContext) KMSProvider() string { + panic(cseNotSupportedMsg) +} + // BytesNeeded returns the number of bytes that should be received from the KMS. // After sending the message to the KMS, this message should be called in a loop until the number returned is 0. func (kc *KmsContext) BytesNeeded() int32 { @@ -31,8 +37,3 @@ func (kc *KmsContext) BytesNeeded() int32 { func (kc *KmsContext) FeedResponse(response []byte) error { panic(cseNotSupportedMsg) } - -// createErrorFromStatus creates a new Error from the status of the KmsContext instance. -func (kc *KmsContext) createErrorFromStatus() error { - panic(cseNotSupportedMsg) -} diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_not_enabled.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_not_enabled.go index d91cc674..92f49976 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_not_enabled.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt_not_enabled.go @@ -4,6 +4,7 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +//go:build !cse // +build !cse package mongocrypt diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/ocsp/ocsp.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/ocsp/ocsp.go index 764dcb43..ed625706 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/ocsp/ocsp.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/ocsp/ocsp.go @@ -18,7 +18,6 @@ import ( "io/ioutil" "math/big" "net/http" - "net/url" "time" "golang.org/x/crypto/ocsp" @@ -28,9 +27,6 @@ import ( var ( tlsFeatureExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24} mustStapleFeatureValue = big.NewInt(5) - - defaultRequestTimeout = 5 * time.Second - errGotOCSPResponse = errors.New("done") ) // Error represents an OCSP verification error @@ -126,10 +122,7 @@ func getParsedResponse(ctx context.Context, cfg config, connState tls.Connection if cfg.disableEndpointChecking { return nil, nil } - externalResponse, err := contactResponders(ctx, cfg) - if err != nil { - return nil, err - } + externalResponse := contactResponders(ctx, cfg) if externalResponse == nil { // None of the responders were available. return nil, nil @@ -210,33 +203,21 @@ func isMustStapleCertificate(cert *x509.Certificate) (bool, error) { return false, nil } -// contactResponders will send a request to the OCSP responders reported by cfg.serverCert. The first response that -// conclusively identifies cfg.serverCert as good or revoked will be returned. If all responders are unavailable or no -// responder returns a conclusive status, (nil, nil) will be returned. -func contactResponders(ctx context.Context, cfg config) (*ResponseDetails, error) { +// contactResponders will send a request to all OCSP responders reported by cfg.serverCert. The +// first response that conclusively identifies cfg.serverCert as good or revoked will be returned. +// If all responders are unavailable or no responder returns a conclusive status, it returns nil. +// contactResponders will wait for up to 5 seconds to get a certificate status response. +func contactResponders(ctx context.Context, cfg config) *ResponseDetails { if len(cfg.serverCert.OCSPServer) == 0 { - return nil, nil + return nil } - requestCtx := ctx // Either ctx or a new context derived from ctx with a five second timeout. - userContextUsed := true - var cancelFn context.CancelFunc - - // Use a context with defaultRequestTimeout if ctx does not have a deadline set or the current deadline is further - // out than defaultRequestTimeout. If the current deadline is less than less than defaultRequestTimeout out, respect - // it. Calling context.WithTimeout would do this for us, but we need to know which context we're using. - wantDeadline := time.Now().Add(defaultRequestTimeout) - if deadline, ok := ctx.Deadline(); !ok || deadline.After(wantDeadline) { - userContextUsed = false - requestCtx, cancelFn = context.WithDeadline(ctx, wantDeadline) - } - defer func() { - if cancelFn != nil { - cancelFn() - } - }() + // Limit all OCSP responder calls to a maximum of 5 seconds or when the passed-in context expires, + // whichever happens first. + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() - group, groupCtx := errgroup.WithContext(requestCtx) + group, ctx := errgroup.WithContext(ctx) ocspResponses := make(chan *ocsp.Response, len(cfg.serverCert.OCSPServer)) defer close(ocspResponses) @@ -244,6 +225,11 @@ func contactResponders(ctx context.Context, cfg config) (*ResponseDetails, error // Re-assign endpoint so it gets re-scoped rather than using the iteration variable in the goroutine. See // https://golang.org/doc/faq#closures_and_goroutines. endpoint := endpoint + + // Start a group of goroutines that each attempt to request the certificate status from one + // of the OCSP endpoints listed in the server certificate. We want to "soft fail" on all + // errors, so this function never returns actual errors. Only a "done" error is returned + // when a response is received so the errgroup cancels any other in-progress requests. group.Go(func() error { // Use bytes.NewReader instead of bytes.NewBuffer because a bytes.Buffer is an owning representation and the // docs recommend not using the underlying []byte after creating the buffer, so a new copy of the request @@ -252,35 +238,16 @@ func contactResponders(ctx context.Context, cfg config) (*ResponseDetails, error if err != nil { return nil } - request = request.WithContext(groupCtx) - - // Execute the request and handle errors as follows: - // - // 1. If the original context expired or was cancelled, propagate the error up so the caller will abort the - // verification and return control to the user. - // - // 2. If any other errors occurred, including the defaultRequestTimeout expiring, or the response has a - // non-200 status code, suppress the error because we want to ignore this responder and wait for a different - // one to responsd. + request = request.WithContext(ctx) + httpResponse, err := http.DefaultClient.Do(request) if err != nil { - urlErr, ok := err.(*url.Error) - if !ok { - return nil - } - - timeout := urlErr.Timeout() - cancelled := urlErr.Err == context.Canceled // Timeout() does not return true for context.Cancelled. - if cancelled || (userContextUsed && timeout) { - // Handle the original context expiring or being cancelled. The url.Error type supports Unwrap, so - // users can use errors.Is to check for context errors. - return err - } - return nil // Ignore all other errors. + return nil } defer func() { _ = httpResponse.Body.Close() }() + if httpResponse.StatusCode != 200 { return nil } @@ -292,26 +259,27 @@ func contactResponders(ctx context.Context, cfg config) (*ResponseDetails, error ocspResponse, err := ocsp.ParseResponseForCert(httpBytes, cfg.serverCert, cfg.issuer) if err != nil || verifyResponse(cfg, ocspResponse) != nil || ocspResponse.Status == ocsp.Unknown { - // If there was an error parsing/validating the response or the response was inconclusive, suppress - // the error because we want to ignore this responder. + // If there was an error parsing/validating the response or the response was + // inconclusive, suppress the error because we want to ignore this responder. return nil } - // Store the response and return a sentinel error so the error group will exit and any in-flight requests - // will be cancelled. + // Send the conclusive response on the response channel and return a "done" error that + // will cause the errgroup to cancel all other in-progress requests. ocspResponses <- ocspResponse - return errGotOCSPResponse + return errors.New("done") }) } - if err := group.Wait(); err != nil && err != errGotOCSPResponse { - return nil, err - } - if len(ocspResponses) == 0 { - // None of the responders gave a conclusive response. - return nil, nil + _ = group.Wait() + select { + case res := <-ocspResponses: + return extractResponseDetails(res) + default: + // If there is no OCSP response on the response channel, all OCSP calls either failed or + // were inconclusive. Return nil. + return nil } - return extractResponseDetails(<-ocspResponses), nil } // verifyResponse checks that the provided OCSP response is valid. diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation.go index 6a33fca5..f1647bef 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation.go @@ -13,6 +13,7 @@ import ( "go.mongodb.org/mongo-driver/bson/bsontype" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/event" + "go.mongodb.org/mongo-driver/internal" "go.mongodb.org/mongo-driver/mongo/description" "go.mongodb.org/mongo-driver/mongo/readconcern" "go.mongodb.org/mongo-driver/mongo/readpref" @@ -46,6 +47,11 @@ const ( readSnapshotMinWireVersion int32 = 13 ) +// RetryablePoolError is a connection pool error that can be retried while executing an operation. +type RetryablePoolError interface { + Retryable() bool +} + // InvalidOperationError is returned from Validate and indicates that a required field is missing // from an instance of Operation. type InvalidOperationError struct{ MissingField string } @@ -72,20 +78,22 @@ type startedInformation struct { cmdName string documentSequenceIncluded bool connID string + serverConnID *int32 redacted bool serviceID *primitive.ObjectID } // finishedInformation keeps track of all of the information necessary for monitoring success and failure events. type finishedInformation struct { - cmdName string - requestID int32 - response bsoncore.Document - cmdErr error - connID string - startTime time.Time - redacted bool - serviceID *primitive.ObjectID + cmdName string + requestID int32 + response bsoncore.Document + cmdErr error + connID string + serverConnID *int32 + startTime time.Time + redacted bool + serviceID *primitive.ObjectID } // ResponseInfo contains the context required to parse a server response. @@ -194,18 +202,22 @@ type Operation struct { CommandMonitor *event.CommandMonitor // Crypt specifies a Crypt object to use for automatic client side encryption and decryption. - Crypt *Crypt + Crypt Crypt // ServerAPI specifies options used to configure the API version sent to the server. ServerAPI *ServerAPIOptions + // IsOutputAggregate specifies whether this operation is an aggregate with an output stage. If true, + // read preference will not be added to the command on wire versions < 13. + IsOutputAggregate bool + // cmdName is only set when serializing OP_MSG and is used internally in readWireMessage. cmdName string } // shouldEncrypt returns true if this operation should automatically be encrypted. func (op Operation) shouldEncrypt() bool { - return op.Crypt != nil && !op.Crypt.BypassAutoEncryption + return op.Crypt != nil && !op.Crypt.BypassAutoEncryption() } // selectServer handles performing server selection for an operation. @@ -298,40 +310,8 @@ func (op Operation) Execute(ctx context.Context, scratch []byte) error { } } - srvr, conn, err := op.getServerAndConnection(ctx) - if err != nil { - return err - } - defer conn.Close() - - desc := description.SelectedServer{Server: conn.Description(), Kind: op.Deployment.Kind()} - scratch = scratch[:0] - - if desc.WireVersion == nil || desc.WireVersion.Max < 4 { - switch op.Legacy { - case LegacyFind: - return op.legacyFind(ctx, scratch, srvr, conn, desc) - case LegacyGetMore: - return op.legacyGetMore(ctx, scratch, srvr, conn, desc) - case LegacyKillCursors: - return op.legacyKillCursors(ctx, scratch, srvr, conn, desc) - } - } - if desc.WireVersion == nil || desc.WireVersion.Max < 3 { - switch op.Legacy { - case LegacyListCollections: - return op.legacyListCollections(ctx, scratch, srvr, conn, desc) - case LegacyListIndexes: - return op.legacyListIndexes(ctx, scratch, srvr, conn, desc) - } - } - - var res bsoncore.Document - var operationErr WriteCommandError - var original error var retries int - retryable := op.retryable(desc.Server) - if retryable && op.RetryMode != nil { + if op.RetryMode != nil { switch op.Type { case Write: if op.Client == nil { @@ -343,15 +323,6 @@ func (op Operation) Execute(ctx context.Context, scratch []byte) error { case RetryContext: retries = -1 } - - op.Client.RetryWrite = false - if *op.RetryMode > RetryNone { - op.Client.RetryWrite = true - if !op.Client.Committing && !op.Client.Aborting { - op.Client.IncrementTxnNumber() - } - } - case Read: switch *op.RetryMode { case RetryOnce, RetryOncePerCommand: @@ -361,10 +332,107 @@ func (op Operation) Execute(ctx context.Context, scratch []byte) error { } } } + + var srvr Server + var conn Connection + var res bsoncore.Document + var operationErr WriteCommandError + var prevErr error batching := op.Batches.Valid() retryEnabled := op.RetryMode != nil && op.RetryMode.Enabled() + retrySupported := false + first := true currIndex := 0 + + // resetForRetry records the error that caused the retry, decrements retries, and resets the + // retry loop variables to request a new server and a new connection for the next attempt. + resetForRetry := func(err error) { + retries-- + prevErr = err + // If we got a connection, close it immediately to release pool resources for + // subsequent retries. + if conn != nil { + conn.Close() + } + // Set the server and connection to nil to request a new server and connection. + srvr = nil + conn = nil + } + for { + // If the server or connection are nil, try to select a new server and get a new connection. + if srvr == nil || conn == nil { + srvr, conn, err = op.getServerAndConnection(ctx) + if err != nil { + // If the returned error is retryable and there are retries remaining (negative + // retries means retry indefinitely), then retry the operation. Set the server + // and connection to nil to request a new server and connection. + if rerr, ok := err.(RetryablePoolError); ok && rerr.Retryable() && retries != 0 { + resetForRetry(err) + continue + } + + // If this is a retry and there's an error from a previous attempt, return the previous + // error instead of the current connection error. + if prevErr != nil { + return prevErr + } + return err + } + defer conn.Close() + } + + // Run steps that must only be run on the first attempt, but not again for retries. + if first { + // Determine if retries are supported for the current operation on the current server + // description. Per the retryable writes specification, only determine this for the + // first server selected: + // + // If the server selected for the first attempt of a retryable write operation does + // not support retryable writes, drivers MUST execute the write as if retryable writes + // were not enabled. + retrySupported = op.retryable(conn.Description()) + + // If retries are supported for the current operation on the current server description, + // client retries are enabled, the operation type is write, and we haven't incremented + // the txn number yet, enable retry writes on the session and increment the txn number. + // Calling IncrementTxnNumber() for server descriptions or topologies that do not + // support retries (e.g. standalone topologies) will cause server errors. Only do this + // check for the first attempt to keep retried writes in the same transaction. + if retrySupported && op.RetryMode != nil && op.Type == Write && op.Client != nil { + op.Client.RetryWrite = false + if op.RetryMode.Enabled() { + op.Client.RetryWrite = true + if !op.Client.Committing && !op.Client.Aborting { + op.Client.IncrementTxnNumber() + } + } + } + + first = false + } + + desc := description.SelectedServer{Server: conn.Description(), Kind: op.Deployment.Kind()} + scratch = scratch[:0] + if desc.WireVersion == nil || desc.WireVersion.Max < 4 { + switch op.Legacy { + case LegacyFind: + return op.legacyFind(ctx, scratch, srvr, conn, desc) + case LegacyGetMore: + return op.legacyGetMore(ctx, scratch, srvr, conn, desc) + case LegacyKillCursors: + return op.legacyKillCursors(ctx, scratch, srvr, conn, desc) + } + } + if desc.WireVersion == nil || desc.WireVersion.Max < 3 { + switch op.Legacy { + case LegacyListCollections: + return op.legacyListCollections(ctx, scratch, srvr, conn, desc) + case LegacyListIndexes: + return op.legacyListIndexes(ctx, scratch, srvr, conn, desc) + } + } + if batching { targetBatchSize := desc.MaxDocumentSize maxDocSize := desc.MaxDocumentSize @@ -398,6 +466,7 @@ func (op Operation) Execute(ctx context.Context, scratch []byte) error { op.cmdName = startedInfo.cmdName startedInfo.redacted = op.redactCommand(startedInfo.cmdName, startedInfo.cmd) startedInfo.serviceID = conn.Description().ServiceID + startedInfo.serverConnID = conn.ServerConnectionID() op.publishStartedEvent(ctx, startedInfo) // get the moreToCome flag information before we compress @@ -412,23 +481,32 @@ func (op Operation) Execute(ctx context.Context, scratch []byte) error { } finishedInfo := finishedInformation{ - cmdName: startedInfo.cmdName, - requestID: startedInfo.requestID, - startTime: time.Now(), - connID: startedInfo.connID, - redacted: startedInfo.redacted, - serviceID: startedInfo.serviceID, + cmdName: startedInfo.cmdName, + requestID: startedInfo.requestID, + startTime: time.Now(), + connID: startedInfo.connID, + serverConnID: startedInfo.serverConnID, + redacted: startedInfo.redacted, + serviceID: startedInfo.serviceID, } - // roundtrip using either the full roundTripper or a special one for when the moreToCome - // flag is set - var roundTrip = op.roundTrip - if moreToCome { - roundTrip = op.moreToComeRoundTrip - } - res, err = roundTrip(ctx, conn, wm) - if ep, ok := srvr.(ErrorProcessor); ok { - _ = ep.ProcessError(err, conn) + // Check if there's enough time to perform a best-case network round trip before the Context + // deadline. If not, create a network error that wraps a context.DeadlineExceeded error and + // skip the actual round trip. + if deadline, ok := ctx.Deadline(); ok && time.Now().Add(srvr.MinRTT()).After(deadline) { + err = op.networkError(context.DeadlineExceeded) + } else { + // roundtrip using either the full roundTripper or a special one for when the moreToCome + // flag is set + var roundTrip = op.roundTrip + if moreToCome { + roundTrip = op.moreToComeRoundTrip + } + res, err = roundTrip(ctx, conn, wm) + + if ep, ok := srvr.(ErrorProcessor); ok { + _ = ep.ProcessError(err, conn) + } } finishedInfo.response = res @@ -438,7 +516,7 @@ func (op Operation) Execute(ctx context.Context, scratch []byte) error { var perr error switch tt := err.(type) { case WriteCommandError: - if e := err.(WriteCommandError); retryable && op.Type == Write && e.UnsupportedStorageEngine() { + if e := err.(WriteCommandError); retrySupported && op.Type == Write && e.UnsupportedStorageEngine() { return ErrUnsupportedStorageEngine } @@ -453,23 +531,16 @@ func (op Operation) Execute(ctx context.Context, scratch []byte) error { tt.Labels = append(tt.Labels, RetryableWriteError) } - if retryable && retryableErr && retries != 0 { - retries-- - original, err = err, nil - conn.Close() // Avoid leaking the connection. - srvr, conn, err = op.getServerAndConnection(ctx) - if err != nil || conn == nil || !op.retryable(conn.Description()) { - if conn != nil { - conn.Close() - } - return original - } - defer conn.Close() // Avoid leaking the new connection. + // If retries are supported for the current operation on the first server description, + // the error is considered retryable, and there are retries remaining (negative retries + // means retry indefinitely), then retry the operation. + if retrySupported && retryableErr && retries != 0 { if op.Client != nil && op.Client.Committing { // Apply majority write concern for retries op.Client.UpdateCommitTransactionWriteConcern() op.WriteConcern = op.Client.CurrentWc } + resetForRetry(tt) continue } @@ -482,7 +553,7 @@ func (op Operation) Execute(ctx context.Context, scratch []byte) error { ConnectionDescription: desc.Server, CurrentIndex: currIndex, } - perr = op.ProcessResponseFn(info) + _ = op.ProcessResponseFn(info) } if batching && len(tt.WriteErrors) > 0 && currIndex > 0 { @@ -493,7 +564,7 @@ func (op Operation) Execute(ctx context.Context, scratch []byte) error { // If batching is enabled and either ordered is the default (which is true) or // explicitly set to true and we have write errors, return the errors. - if batching && (op.Batches.Ordered == nil || *op.Batches.Ordered == true) && len(tt.WriteErrors) > 0 { + if batching && (op.Batches.Ordered == nil || *op.Batches.Ordered) && len(tt.WriteErrors) > 0 { return tt } if op.Client != nil && op.Client.Committing && tt.WriteConcernError != nil { @@ -503,6 +574,7 @@ func (op Operation) Execute(ctx context.Context, scratch []byte) error { Code: int32(tt.WriteConcernError.Code), Message: tt.WriteConcernError.Message, Labels: tt.Labels, + Raw: tt.Raw, } // The UnknownTransactionCommitResult label is added to all writeConcernErrors besides unknownReplWriteConcernCode // and unsatisfiableWriteConcernCode @@ -517,13 +589,15 @@ func (op Operation) Execute(ctx context.Context, scratch []byte) error { operationErr.WriteConcernError = tt.WriteConcernError operationErr.WriteErrors = append(operationErr.WriteErrors, tt.WriteErrors...) operationErr.Labels = tt.Labels + operationErr.Raw = tt.Raw case Error: if tt.HasErrorLabel(TransientTransactionError) || tt.HasErrorLabel(UnknownTransactionCommitResult) { if err := op.Client.ClearPinnedResources(); err != nil { return err } } - if e := err.(Error); retryable && op.Type == Write && e.UnsupportedStorageEngine() { + + if e := err.(Error); retrySupported && op.Type == Write && e.UnsupportedStorageEngine() { return ErrUnsupportedStorageEngine } @@ -544,23 +618,16 @@ func (op Operation) Execute(ctx context.Context, scratch []byte) error { retryableErr = tt.RetryableRead() } - if retryable && retryableErr && retries != 0 { - retries-- - original, err = err, nil - conn.Close() // Avoid leaking the connection. - srvr, conn, err = op.getServerAndConnection(ctx) - if err != nil || conn == nil || !op.retryable(conn.Description()) { - if conn != nil { - conn.Close() - } - return original - } - defer conn.Close() // Avoid leaking the new connection. + // If retries are supported for the current operation on the first server description, + // the error is considered retryable, and there are retries remaining (negative retries + // means retry indefinitely), then retry the operation. + if retrySupported && retryableErr && retries != 0 { if op.Client != nil && op.Client.Committing { // Apply majority write concern for retries op.Client.UpdateCommitTransactionWriteConcern() op.WriteConcern = op.Client.CurrentWc } + resetForRetry(tt) continue } @@ -573,7 +640,7 @@ func (op Operation) Execute(ctx context.Context, scratch []byte) error { ConnectionDescription: desc.Server, CurrentIndex: currIndex, } - perr = op.ProcessResponseFn(info) + _ = op.ProcessResponseFn(info) } if op.Client != nil && op.Client.Committing && (retryableErr || tt.Code == 50) { @@ -607,13 +674,16 @@ func (op Operation) Execute(ctx context.Context, scratch []byte) error { ConnectionDescription: desc.Server, CurrentIndex: currIndex, } - perr = op.ProcessResponseFn(info) + _ = op.ProcessResponseFn(info) } return err } + // If we're batching and there are batches remaining, advance to the next batch. This isn't + // a retry, so increment the transaction number, reset the retries number, and don't set + // server or connection to nil to continue using the same connection. if batching && len(op.Batches.Documents) > 0 { - if retryable && op.Client != nil && op.RetryMode != nil { + if retrySupported && op.Client != nil && op.RetryMode != nil { if *op.RetryMode > RetryNone { op.Client.IncrementTxnNumber() } @@ -664,17 +734,7 @@ func (op Operation) retryable(desc description.Server) bool { func (op Operation) roundTrip(ctx context.Context, conn Connection, wm []byte) ([]byte, error) { err := conn.WriteWireMessage(ctx, wm) if err != nil { - labels := []string{NetworkError} - if op.Client != nil { - op.Client.MarkDirty() - } - if op.Client != nil && op.Client.TransactionRunning() && !op.Client.Committing { - labels = append(labels, TransientTransactionError) - } - if op.Client != nil && op.Client.Committing { - labels = append(labels, UnknownTransactionCommitResult) - } - return nil, Error{Message: err.Error(), Labels: labels, Wrapped: err} + return nil, op.networkError(err) } return op.readWireMessage(ctx, conn, wm) @@ -685,17 +745,7 @@ func (op Operation) readWireMessage(ctx context.Context, conn Connection, wm []b wm, err = conn.ReadWireMessage(ctx, wm[:0]) if err != nil { - labels := []string{NetworkError} - if op.Client != nil { - op.Client.MarkDirty() - } - if op.Client != nil && op.Client.TransactionRunning() && !op.Client.Committing { - labels = append(labels, TransientTransactionError) - } - if op.Client != nil && op.Client.Committing { - labels = append(labels, UnknownTransactionCommitResult) - } - return nil, Error{Message: err.Error(), Labels: labels, Wrapped: err} + return nil, op.networkError(err) } // If we're using a streamable connection, we set its streaming state based on the moreToCome flag in the server @@ -734,6 +784,27 @@ func (op Operation) readWireMessage(ctx context.Context, conn Connection, wm []b return res, nil } +// networkError wraps the provided error in an Error with label "NetworkError" and, if a transaction +// is running or committing, the appropriate transaction state labels. The returned error indicates +// the operation should be retried for reads and writes. If err is nil, networkError returns nil. +func (op Operation) networkError(err error) error { + if err == nil { + return nil + } + + labels := []string{NetworkError} + if op.Client != nil { + op.Client.MarkDirty() + } + if op.Client != nil && op.Client.TransactionRunning() && !op.Client.Committing { + labels = append(labels, TransientTransactionError) + } + if op.Client != nil && op.Client.Committing { + labels = append(labels, UnknownTransactionCommitResult) + } + return Error{Message: err.Error(), Labels: labels, Wrapped: err} +} + // moreToComeRoundTrip writes a wiremessage to the provided connection. This is used when an OP_MSG is // being sent with the moreToCome bit set. func (op *Operation) moreToComeRoundTrip(ctx context.Context, conn Connection, wm []byte) ([]byte, error) { @@ -795,8 +866,10 @@ func (Operation) decompressWireMessage(wm []byte) ([]byte, error) { func (op Operation) createWireMessage(ctx context.Context, dst []byte, desc description.SelectedServer, conn Connection) ([]byte, startedInformation, error) { - - if desc.WireVersion == nil || desc.WireVersion.Max < wiremessage.OpmsgWireVersion { + // If topology is not LoadBalanced, API version is not declared, and wire version is unknown + // or less than 6, use OP_QUERY. Otherwise, use OP_MSG. + if desc.Kind != description.LoadBalanced && op.ServerAPI == nil && + (desc.WireVersion == nil || desc.WireVersion.Max < wiremessage.OpmsgWireVersion) { return op.createQueryWireMessage(dst, desc) } return op.createMsgWireMessage(ctx, dst, desc, conn) @@ -813,7 +886,7 @@ func (op Operation) addBatchArray(dst []byte) []byte { func (op Operation) createQueryWireMessage(dst []byte, desc description.SelectedServer) ([]byte, startedInformation, error) { var info startedInformation - flags := op.slaveOK(desc) + flags := op.secondaryOK(desc) var wmindex int32 info.requestID = wiremessage.NextRequestID() wmindex, dst = wiremessage.AppendHeaderStart(dst, info.requestID, 0, wiremessage.OpQuery) @@ -826,7 +899,7 @@ func (op Operation) createQueryWireMessage(dst []byte, desc description.Selected dst = wiremessage.AppendQueryNumberToReturn(dst, -1) wrapper := int32(-1) - rp, err := op.createReadPref(desc.Server.Kind, desc.Kind, true) + rp, err := op.createReadPref(desc, true) if err != nil { return dst, info, err } @@ -924,7 +997,7 @@ func (op Operation) createMsgWireMessage(ctx context.Context, dst []byte, desc d dst = op.addServerAPI(dst) dst = bsoncore.AppendStringElement(dst, "$db", op.Database) - rp, err := op.createReadPref(desc.Server.Kind, desc.Kind, false) + rp, err := op.createReadPref(desc, false) if err != nil { return dst, info, err } @@ -1185,9 +1258,17 @@ func (op Operation) getReadPrefBasedOnTransaction() (*readpref.ReadPref, error) return op.ReadPreference, nil } -func (op Operation) createReadPref(serverKind description.ServerKind, topologyKind description.TopologyKind, isOpQuery bool) (bsoncore.Document, error) { - if serverKind == description.Standalone || (isOpQuery && serverKind != description.Mongos) || op.Type == Write { - // Don't send read preference for non-mongos when using OP_QUERY or for all standalones +func (op Operation) createReadPref(desc description.SelectedServer, isOpQuery bool) (bsoncore.Document, error) { + // TODO(GODRIVER-2231): Instead of checking if isOutputAggregate and desc.Server.WireVersion.Max < 13, + // somehow check if supplied readPreference was "overwritten" with primary in description.selectForReplicaSet. + if desc.Server.Kind == description.Standalone || (isOpQuery && desc.Server.Kind != description.Mongos) || + op.Type == Write || (op.IsOutputAggregate && desc.Server.WireVersion.Max < 13) { + // Don't send read preference for: + // 1. all standalones + // 2. non-mongos when using OP_QUERY + // 3. all writes + // 4. when operation is an aggregate with an output stage, and selected server's wire + // version is < 13 return nil, nil } @@ -1198,7 +1279,7 @@ func (op Operation) createReadPref(serverKind description.ServerKind, topologyKi } if rp == nil { - if topologyKind == description.Single && serverKind != description.Mongos { + if desc.Kind == description.Single && desc.Server.Kind != description.Mongos { doc = bsoncore.AppendStringElement(doc, "mode", "primaryPreferred") doc, _ = bsoncore.AppendDocumentEnd(doc, idx) return doc, nil @@ -1208,10 +1289,10 @@ func (op Operation) createReadPref(serverKind description.ServerKind, topologyKi switch rp.Mode() { case readpref.PrimaryMode: - if serverKind == description.Mongos { + if desc.Server.Kind == description.Mongos { return nil, nil } - if topologyKind == description.Single { + if desc.Kind == description.Single { doc = bsoncore.AppendStringElement(doc, "mode", "primaryPreferred") doc, _ = bsoncore.AppendDocumentEnd(doc, idx) return doc, nil @@ -1221,7 +1302,7 @@ func (op Operation) createReadPref(serverKind description.ServerKind, topologyKi doc = bsoncore.AppendStringElement(doc, "mode", "primaryPreferred") case readpref.SecondaryPreferredMode: _, ok := rp.MaxStaleness() - if serverKind == description.Mongos && isOpQuery && !ok && len(rp.TagSets()) == 0 && rp.HedgeEnabled() == nil { + if desc.Server.Kind == description.Mongos && isOpQuery && !ok && len(rp.TagSets()) == 0 && rp.HedgeEnabled() == nil { return nil, nil } doc = bsoncore.AppendStringElement(doc, "mode", "secondaryPreferred") @@ -1267,20 +1348,20 @@ func (op Operation) createReadPref(serverKind description.ServerKind, topologyKi return doc, nil } -func (op Operation) slaveOK(desc description.SelectedServer) wiremessage.QueryFlag { +func (op Operation) secondaryOK(desc description.SelectedServer) wiremessage.QueryFlag { if desc.Kind == description.Single && desc.Server.Kind != description.Mongos { - return wiremessage.SlaveOK + return wiremessage.SecondaryOK } if rp := op.ReadPreference; rp != nil && rp.Mode() != readpref.PrimaryMode { - return wiremessage.SlaveOK + return wiremessage.SecondaryOK } return 0 } func (Operation) canCompress(cmd string) bool { - if cmd == "isMaster" || cmd == "saslStart" || cmd == "saslContinue" || cmd == "getnonce" || cmd == "authenticate" || + if cmd == internal.LegacyHello || cmd == "hello" || cmd == "saslStart" || cmd == "saslContinue" || cmd == "getnonce" || cmd == "authenticate" || cmd == "createUser" || cmd == "updateUser" || cmd == "copydbSaslStart" || cmd == "copydbgetnonce" || cmd == "copydb" { return false } @@ -1436,11 +1517,11 @@ func (op *Operation) redactCommand(cmd string, doc bsoncore.Document) bool { return true } - if strings.ToLower(cmd) != "ismaster" && cmd != "hello" { + if strings.ToLower(cmd) != internal.LegacyHelloLowercase && cmd != "hello" { return false } - // An isMaster without speculative authentication can be monitored. + // A hello without speculative authentication can be monitored. _, err := doc.LookupErr("speculativeAuthenticate") return err == nil } @@ -1467,12 +1548,13 @@ func (op Operation) publishStartedEvent(ctx context.Context, info startedInforma } started := &event.CommandStartedEvent{ - Command: cmdCopy, - DatabaseName: op.Database, - CommandName: info.cmdName, - RequestID: int64(info.requestID), - ConnectionID: info.connID, - ServiceID: info.serviceID, + Command: cmdCopy, + DatabaseName: op.Database, + CommandName: info.cmdName, + RequestID: int64(info.requestID), + ConnectionID: info.connID, + ServerConnectionID: info.serverConnID, + ServiceID: info.serviceID, } op.CommandMonitor.Started(ctx, started) } @@ -1491,15 +1573,16 @@ func (op Operation) publishFinishedEvent(ctx context.Context, info finishedInfor var durationNanos int64 var emptyTime time.Time if info.startTime != emptyTime { - durationNanos = time.Now().Sub(info.startTime).Nanoseconds() + durationNanos = time.Since(info.startTime).Nanoseconds() } finished := event.CommandFinishedEvent{ - CommandName: info.cmdName, - RequestID: int64(info.requestID), - ConnectionID: info.connID, - DurationNanos: durationNanos, - ServiceID: info.serviceID, + CommandName: info.cmdName, + RequestID: int64(info.requestID), + ConnectionID: info.connID, + DurationNanos: durationNanos, + ServerConnectionID: info.serverConnID, + ServiceID: info.serviceID, } if success { diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/abort_transaction.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/abort_transaction.go index b1957a3e..608733b4 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/abort_transaction.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/abort_transaction.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -27,7 +25,7 @@ type AbortTransaction struct { clock *session.ClusterClock collection string monitor *event.CommandMonitor - crypt *driver.Crypt + crypt driver.Crypt database string deployment driver.Deployment selector description.ServerSelector @@ -46,7 +44,7 @@ func (at *AbortTransaction) processResponse(driver.ResponseInfo) error { return err } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (at *AbortTransaction) Execute(ctx context.Context) error { if at.deployment == nil { return errors.New("the AbortTransaction operation must have a Deployment set before Execute can be called") @@ -130,7 +128,7 @@ func (at *AbortTransaction) CommandMonitor(monitor *event.CommandMonitor) *Abort } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (at *AbortTransaction) Crypt(crypt *driver.Crypt) *AbortTransaction { +func (at *AbortTransaction) Crypt(crypt driver.Crypt) *AbortTransaction { if at == nil { at = new(AbortTransaction) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/abort_transaction.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/abort_transaction.toml deleted file mode 100644 index d4789dd3..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/abort_transaction.toml +++ /dev/null @@ -1,17 +0,0 @@ -version = 0 -name = "AbortTransaction" -documentation = "AbortTransaction performs an abortTransaction operation." - -[properties] -enabled = ["write concern"] -retryable = {mode = "once per command", type = "writes"} - -[command] -name = "abortTransaction" -parameter = "database" - -[request.recoveryToken] -type = "document" -documentation = """ -RecoveryToken sets the recovery token to use when committing or aborting a sharded transaction.\ -""" diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/aggregate.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/aggregate.go index 311d2874..e3554e33 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/aggregate.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/aggregate.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -23,7 +21,7 @@ import ( "go.mongodb.org/mongo-driver/x/mongo/driver/session" ) -// Performs an aggregate operation +// Aggregate represents an aggregate operation. type Aggregate struct { allowDiskUse *bool batchSize *int32 @@ -44,9 +42,11 @@ type Aggregate struct { retry *driver.RetryMode selector description.ServerSelector writeConcern *writeconcern.WriteConcern - crypt *driver.Crypt + crypt driver.Crypt serverAPI *driver.ServerAPIOptions let bsoncore.Document + hasOutputStage bool + customOptions map[string]bsoncore.Value result driver.CursorResponse } @@ -68,6 +68,8 @@ func (a *Aggregate) Result(opts driver.CursorOptions) (*driver.BatchCursor, erro return driver.NewBatchCursor(a.result, clientSession, clock, opts) } +// ResultCursorResponse returns the underlying CursorResponse result of executing this +// operation. func (a *Aggregate) ResultCursorResponse() driver.CursorResponse { return a.result } @@ -80,7 +82,7 @@ func (a *Aggregate) processResponse(info driver.ResponseInfo) error { } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (a *Aggregate) Execute(ctx context.Context) error { if a.deployment == nil { return errors.New("the Aggregate operation must have a Deployment set before Execute can be called") @@ -104,6 +106,7 @@ func (a *Aggregate) Execute(ctx context.Context) error { Crypt: a.crypt, MinimumWriteConcernWireVersion: 5, ServerAPI: a.serverAPI, + IsOutputAggregate: a.hasOutputStage, }.Execute(ctx, nil) } @@ -153,6 +156,9 @@ func (a *Aggregate) command(dst []byte, desc description.SelectedServer) ([]byte if a.let != nil { dst = bsoncore.AppendDocumentElement(dst, "let", a.let) } + for optionName, optionValue := range a.customOptions { + dst = bsoncore.AppendValueElement(dst, optionName, optionValue) + } cursorDoc, _ = bsoncore.AppendDocumentEnd(cursorDoc, cursorIdx) dst = bsoncore.AppendDocumentElement(dst, "cursor", cursorDoc) @@ -352,7 +358,7 @@ func (a *Aggregate) Retry(retry driver.RetryMode) *Aggregate { } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (a *Aggregate) Crypt(crypt *driver.Crypt) *Aggregate { +func (a *Aggregate) Crypt(crypt driver.Crypt) *Aggregate { if a == nil { a = new(Aggregate) } @@ -380,3 +386,24 @@ func (a *Aggregate) Let(let bsoncore.Document) *Aggregate { a.let = let return a } + +// HasOutputStage specifies whether the aggregate contains an output stage. Used in determining when to +// append read preference at the operation level. +func (a *Aggregate) HasOutputStage(hos bool) *Aggregate { + if a == nil { + a = new(Aggregate) + } + + a.hasOutputStage = hos + return a +} + +// CustomOptions specifies extra options to use in the aggregate command. +func (a *Aggregate) CustomOptions(co map[string]bsoncore.Value) *Aggregate { + if a == nil { + a = new(Aggregate) + } + + a.customOptions = co + return a +} diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/aggregate.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/aggregate.toml deleted file mode 100644 index fd397b9c..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/aggregate.toml +++ /dev/null @@ -1,48 +0,0 @@ -version = 0 -name = "Aggregate" -documentation = "Performs an aggregate operation" -response.type = "batch cursor" - -[properties] -enabled = ["read concern", "read preference", "write concern"] -retryable = {mode = "once per command", type = "reads"} -MinimumWriteConcernWireVersion = 5 - -[command] -name = "aggregate" -parameter = "collection" -database = true - -[request.pipeline] -type = "array" -constructor = true -documentation = "Pipeline determines how data is transformed for an aggregation." - -[request.allowDiskUse] -type = "boolean" -documentation = "AllowDiskUse enables writing to temporary files. When true, aggregation stages can write to the dbPath/_tmp directory." - -[request.batchSize] -type = "int32" -documentation = "BatchSize specifies the number of documents to return in every batch." - -[request.bypassDocumentValidation] -type = "boolean" -documentation = "BypassDocumentValidation allows the write to opt-out of document level validation. This only applies when the $out stage is specified." - -[request.collation] -type = "document" -minWireVersionRequired = 5 -documentation = "Collation specifies a collation. This option is only valid for server versions 3.4 and above." - -[request.maxTimeMS] -type = "int64" -documentation = "MaxTimeMS specifies the maximum amount of time to allow the query to run." - -[request.comment] -type = "string" -documentation = "Comment specifies an arbitrary string to help trace the operation through the database profiler, currentOp, and logs." - -[request.hint] -type = "value" -documentation = "Hint specifies the index to use." diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/command.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/command.go index f2a1ca46..07b21c56 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/command.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/command.go @@ -1,4 +1,8 @@ -// NOTE: This file is maintained by hand because operationgen cannot generate it. +// Copyright (C) MongoDB, Inc. 2021-present. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 package operation @@ -28,9 +32,7 @@ type Command struct { monitor *event.CommandMonitor resultResponse bsoncore.Document resultCursor *driver.BatchCursor - srvr driver.Server - desc description.Server - crypt *driver.Crypt + crypt driver.Crypt serverAPI *driver.ServerAPIOptions createCursor bool cursorOpts driver.CursorOptions @@ -67,7 +69,7 @@ func (c *Command) ResultCursor() (*driver.BatchCursor, error) { return c.resultCursor, nil } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (c *Command) Execute(ctx context.Context) error { if c.deployment == nil { return errors.New("the Command operation must have a Deployment set before Execute can be called") @@ -185,7 +187,7 @@ func (c *Command) ServerSelector(selector description.ServerSelector) *Command { } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (c *Command) Crypt(crypt *driver.Crypt) *Command { +func (c *Command) Crypt(crypt driver.Crypt) *Command { if c == nil { c = new(Command) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/commit_transaction.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/commit_transaction.go index c5605250..14ed7bcd 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/commit_transaction.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/commit_transaction.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -27,7 +25,7 @@ type CommitTransaction struct { session *session.Client clock *session.ClusterClock monitor *event.CommandMonitor - crypt *driver.Crypt + crypt driver.Crypt database string deployment driver.Deployment selector description.ServerSelector @@ -46,7 +44,7 @@ func (ct *CommitTransaction) processResponse(driver.ResponseInfo) error { return err } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (ct *CommitTransaction) Execute(ctx context.Context) error { if ct.deployment == nil { return errors.New("the CommitTransaction operation must have a Deployment set before Execute can be called") @@ -133,7 +131,7 @@ func (ct *CommitTransaction) CommandMonitor(monitor *event.CommandMonitor) *Comm } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (ct *CommitTransaction) Crypt(crypt *driver.Crypt) *CommitTransaction { +func (ct *CommitTransaction) Crypt(crypt driver.Crypt) *CommitTransaction { if ct == nil { ct = new(CommitTransaction) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/commit_transaction.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/commit_transaction.toml deleted file mode 100644 index c49dbd80..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/commit_transaction.toml +++ /dev/null @@ -1,22 +0,0 @@ -version = 0 -name = "CommitTransaction" -documentation = "CommitTransaction attempts to commit a transaction." - -[properties] -enabled = ["write concern"] -disabled = ["collection"] -retryable = {mode = "once per command", type = "writes"} - -[command] -name = "commitTransaction" -parameter = "database" - -[request.recoveryToken] -type = "document" -documentation = """ -RecoveryToken sets the recovery token to use when committing or aborting a sharded transaction.\ -""" - -[request.maxTimeMS] -type = "int64" -documentation = "MaxTimeMS specifies the maximum amount of time to allow the query to run." diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/count.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/count.go index f38d6aa3..8404f364 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/count.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/count.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -22,7 +20,7 @@ import ( "go.mongodb.org/mongo-driver/x/mongo/driver/session" ) -// Performs a count operation +// Count represents a count operation. type Count struct { maxTimeMS *int64 query bsoncore.Document @@ -30,7 +28,7 @@ type Count struct { clock *session.ClusterClock collection string monitor *event.CommandMonitor - crypt *driver.Crypt + crypt driver.Crypt database string deployment driver.Deployment readConcern *readconcern.ReadConcern @@ -41,52 +39,50 @@ type Count struct { serverAPI *driver.ServerAPIOptions } +// CountResult represents a count result returned by the server. type CountResult struct { // The number of documents found N int64 } -func buildCountResult(response bsoncore.Document, srvr driver.Server) (CountResult, error) { +func buildCountResult(response bsoncore.Document) (CountResult, error) { elements, err := response.Elements() if err != nil { return CountResult{}, err } cr := CountResult{} -elementLoop: for _, element := range elements { switch element.Key() { case "n": // for count using original command var ok bool cr.N, ok = element.Value().AsInt64OK() if !ok { - err = fmt.Errorf("response field 'n' is type int64, but received BSON type %s", + return cr, fmt.Errorf("response field 'n' is type int64, but received BSON type %s", element.Value().Type) - break elementLoop } case "cursor": // for count using aggregate with $collStats firstBatch, err := element.Value().Document().LookupErr("firstBatch") if err != nil { - break elementLoop + return cr, err } // get count value from first batch val := firstBatch.Array().Index(0) count, err := val.Document().LookupErr("n") if err != nil { - break elementLoop + return cr, err } // use count as Int64 for result var ok bool cr.N, ok = count.AsInt64OK() if !ok { - err = fmt.Errorf("response field 'n' is type int64, but received BSON type %s", + return cr, fmt.Errorf("response field 'n' is type int64, but received BSON type %s", element.Value().Type) - break elementLoop } } } - return cr, err + return cr, nil } // NewCount constructs and returns a new Count. @@ -99,11 +95,11 @@ func (c *Count) Result() CountResult { return c.result } func (c *Count) processResponse(info driver.ResponseInfo) error { var err error - c.result, err = buildCountResult(info.ServerResponse, info.Server) + c.result, err = buildCountResult(info.ServerResponse) return err } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (c *Count) Execute(ctx context.Context) error { if c.deployment == nil { return errors.New("the Count operation must have a Deployment set before Execute can be called") @@ -238,7 +234,7 @@ func (c *Count) CommandMonitor(monitor *event.CommandMonitor) *Count { } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (c *Count) Crypt(crypt *driver.Crypt) *Count { +func (c *Count) Crypt(crypt driver.Crypt) *Count { if c == nil { c = new(Count) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/count.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/count.toml deleted file mode 100644 index f5d5e032..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/count.toml +++ /dev/null @@ -1,26 +0,0 @@ -version = 0 -name = "Count" -documentation = "Performs a count operation" - -[properties] -enabled = ["read concern", "read preference"] -retryable = {mode = "once per command", type = "reads"} - -[command] -name = "count" -parameter = "collection" - -[request.maxTimeMS] -type = "int64" -documentation = "MaxTimeMS specifies the maximum amount of time to allow the query to run." - -[request.query] -type = "document" -documentation = "Query determines what results are returned from find." - -[response] -name = "CountResult" - -[response.field.n] -type = "int64" -documentation = "The number of documents found" diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/create.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/create.go index a06fac7e..48fee42b 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/create.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/create.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -20,7 +18,7 @@ import ( "go.mongodb.org/mongo-driver/x/mongo/driver/session" ) -// Create a create operation +// Create represents a create operation. type Create struct { capped *bool collation bsoncore.Document @@ -37,7 +35,7 @@ type Create struct { session *session.Client clock *session.ClusterClock monitor *event.CommandMonitor - crypt *driver.Crypt + crypt driver.Crypt database string deployment driver.Deployment selector description.ServerSelector @@ -55,11 +53,10 @@ func NewCreate(collectionName string) *Create { } func (c *Create) processResponse(driver.ResponseInfo) error { - var err error - return err + return nil } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (c *Create) Execute(ctx context.Context) error { if c.deployment == nil { return errors.New("the Create operation must have a Deployment set before Execute can be called") @@ -130,7 +127,7 @@ func (c *Create) command(dst []byte, desc description.SelectedServer) ([]byte, e return dst, nil } -// Specifies if the collection is capped. +// Capped specifies if the collection is capped. func (c *Create) Capped(capped bool) *Create { if c == nil { c = new(Create) @@ -150,7 +147,7 @@ func (c *Create) Collation(collation bsoncore.Document) *Create { return c } -// Specifies the name of the collection to create. +// CollectionName specifies the name of the collection to create. func (c *Create) CollectionName(collectionName string) *Create { if c == nil { c = new(Create) @@ -160,7 +157,7 @@ func (c *Create) CollectionName(collectionName string) *Create { return c } -// Specifies a default configuration for indexes on the collection. +// IndexOptionDefaults specifies a default configuration for indexes on the collection. func (c *Create) IndexOptionDefaults(indexOptionDefaults bsoncore.Document) *Create { if c == nil { c = new(Create) @@ -170,7 +167,7 @@ func (c *Create) IndexOptionDefaults(indexOptionDefaults bsoncore.Document) *Cre return c } -// Specifies the maximum number of documents allowed in a capped collection. +// Max specifies the maximum number of documents allowed in a capped collection. func (c *Create) Max(max int64) *Create { if c == nil { c = new(Create) @@ -180,7 +177,7 @@ func (c *Create) Max(max int64) *Create { return c } -// Specifies the agggregtion pipeline to be run against the source to create the view. +// Pipeline specifies the agggregtion pipeline to be run against the source to create the view. func (c *Create) Pipeline(pipeline bsoncore.Document) *Create { if c == nil { c = new(Create) @@ -190,7 +187,7 @@ func (c *Create) Pipeline(pipeline bsoncore.Document) *Create { return c } -// Specifies the maximum size in bytes for a capped collection. +// Size specifies the maximum size in bytes for a capped collection. func (c *Create) Size(size int64) *Create { if c == nil { c = new(Create) @@ -200,7 +197,7 @@ func (c *Create) Size(size int64) *Create { return c } -// Specifies the storage engine to use for the index. +// StorageEngine specifies the storage engine to use for the index. func (c *Create) StorageEngine(storageEngine bsoncore.Document) *Create { if c == nil { c = new(Create) @@ -210,7 +207,7 @@ func (c *Create) StorageEngine(storageEngine bsoncore.Document) *Create { return c } -// Specifies what should happen if a document being inserted does not pass validation. +// ValidationAction specifies what should happen if a document being inserted does not pass validation. func (c *Create) ValidationAction(validationAction string) *Create { if c == nil { c = new(Create) @@ -220,7 +217,8 @@ func (c *Create) ValidationAction(validationAction string) *Create { return c } -// Specifies how strictly the server applies validation rules to existing documents in the collection during update operations. +// ValidationLevel specifies how strictly the server applies validation rules to existing documents in the collection +// during update operations. func (c *Create) ValidationLevel(validationLevel string) *Create { if c == nil { c = new(Create) @@ -230,7 +228,7 @@ func (c *Create) ValidationLevel(validationLevel string) *Create { return c } -// Specifies validation rules for the collection. +// Validator specifies validation rules for the collection. func (c *Create) Validator(validator bsoncore.Document) *Create { if c == nil { c = new(Create) @@ -240,7 +238,7 @@ func (c *Create) Validator(validator bsoncore.Document) *Create { return c } -// Specifies the name of the source collection or view on which the view will be created. +// ViewOn specifies the name of the source collection or view on which the view will be created. func (c *Create) ViewOn(viewOn string) *Create { if c == nil { c = new(Create) @@ -281,7 +279,7 @@ func (c *Create) CommandMonitor(monitor *event.CommandMonitor) *Create { } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (c *Create) Crypt(crypt *driver.Crypt) *Create { +func (c *Create) Crypt(crypt driver.Crypt) *Create { if c == nil { c = new(Create) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/create.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/create.toml deleted file mode 100644 index c1e29cfc..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/create.toml +++ /dev/null @@ -1,62 +0,0 @@ -version = 0 -name = "Create" -documentation = "Create a create operation" - -[properties] -enabled = ["write concern"] -disabled = ["collection"] - -[command] -name = "create" -parameter = "collectionName" - -[request.collectionName] -type = "string" -documentation = "Specifies the name of the collection to create." -skip = true -constructor = true - -[request.capped] -type = "boolean" -documentation = "Specifies if the collection is capped." - -[request.collation] -type = "document" -minWireVersionRequired = 5 -documentation = "Collation specifies a collation. This option is only valid for server versions 3.4 and above." - -[request.indexOptionDefaults] -type = "document" -documentation = "Specifies a default configuration for indexes on the collection." - -[request.max] -type = "int64" -documentation = "Specifies the maximum number of documents allowed in a capped collection." - -[request.pipeline] -type = "array" -documentation = "Specifies the agggregtion pipeline to be run against the source to create the view." - -[request.size] -type = "int64" -documentation = "Specifies the maximum size in bytes for a capped collection." - -[request.storageEngine] -type = "document" -documentation = "Specifies the storage engine to use for the index." - -[request.validator] -type = "document" -documentation = "Specifies validation rules for the collection." - -[request.validationAction] -type = "string" -documentation = "Specifies what should happen if a document being inserted does not pass validation." - -[request.validationLevel] -type = "string" -documentation = "Specifies how strictly the server applies validation rules to existing documents in the collection during update operations." - -[request.viewOn] -type = "string" -documentation = "Specifies the name of the source collection or view on which the view will be created." diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/createIndexes.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/createIndexes.go index 63d3fc9b..6bf98dee 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/createIndexes.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/createIndexes.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -31,7 +29,7 @@ type CreateIndexes struct { clock *session.ClusterClock collection string monitor *event.CommandMonitor - crypt *driver.Crypt + crypt driver.Crypt database string deployment driver.Deployment selector description.ServerSelector @@ -40,6 +38,7 @@ type CreateIndexes struct { serverAPI *driver.ServerAPIOptions } +// CreateIndexesResult represents a createIndexes result returned by the server. type CreateIndexesResult struct { // If the collection was created automatically. CreatedCollectionAutomatically bool @@ -49,7 +48,7 @@ type CreateIndexesResult struct { IndexesBefore int32 } -func buildCreateIndexesResult(response bsoncore.Document, srvr driver.Server) (CreateIndexesResult, error) { +func buildCreateIndexesResult(response bsoncore.Document) (CreateIndexesResult, error) { elements, err := response.Elements() if err != nil { return CreateIndexesResult{}, err @@ -61,19 +60,19 @@ func buildCreateIndexesResult(response bsoncore.Document, srvr driver.Server) (C var ok bool cir.CreatedCollectionAutomatically, ok = element.Value().BooleanOK() if !ok { - err = fmt.Errorf("response field 'createdCollectionAutomatically' is type bool, but received BSON type %s", element.Value().Type) + return cir, fmt.Errorf("response field 'createdCollectionAutomatically' is type bool, but received BSON type %s", element.Value().Type) } case "indexesAfter": var ok bool cir.IndexesAfter, ok = element.Value().AsInt32OK() if !ok { - err = fmt.Errorf("response field 'indexesAfter' is type int32, but received BSON type %s", element.Value().Type) + return cir, fmt.Errorf("response field 'indexesAfter' is type int32, but received BSON type %s", element.Value().Type) } case "indexesBefore": var ok bool cir.IndexesBefore, ok = element.Value().AsInt32OK() if !ok { - err = fmt.Errorf("response field 'indexesBefore' is type int32, but received BSON type %s", element.Value().Type) + return cir, fmt.Errorf("response field 'indexesBefore' is type int32, but received BSON type %s", element.Value().Type) } } } @@ -92,11 +91,11 @@ func (ci *CreateIndexes) Result() CreateIndexesResult { return ci.result } func (ci *CreateIndexes) processResponse(info driver.ResponseInfo) error { var err error - ci.result, err = buildCreateIndexesResult(info.ServerResponse, info.Server) + ci.result, err = buildCreateIndexesResult(info.ServerResponse) return err } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (ci *CreateIndexes) Execute(ctx context.Context) error { if ci.deployment == nil { return errors.New("the CreateIndexes operation must have a Deployment set before Execute can be called") @@ -135,9 +134,9 @@ func (ci *CreateIndexes) command(dst []byte, desc description.SelectedServer) ([ return dst, nil } -// The number of data-bearing members of a replica set, including the primary, that must complete the index builds -// successfully before the primary marks the indexes as ready. This should either be a string or int32 value. -// +// CommitQuorum specifies the number of data-bearing members of a replica set, including the primary, that must +// complete the index builds successfully before the primary marks the indexes as ready. This should either be a +// string or int32 value. func (ci *CreateIndexes) CommitQuorum(commitQuorum bsoncore.Value) *CreateIndexes { if ci == nil { ci = new(CreateIndexes) @@ -147,7 +146,7 @@ func (ci *CreateIndexes) CommitQuorum(commitQuorum bsoncore.Value) *CreateIndexe return ci } -// An array containing index specification documents for the indexes being created. +// Indexes specifies an array containing index specification documents for the indexes being created. func (ci *CreateIndexes) Indexes(indexes bsoncore.Document) *CreateIndexes { if ci == nil { ci = new(CreateIndexes) @@ -208,7 +207,7 @@ func (ci *CreateIndexes) CommandMonitor(monitor *event.CommandMonitor) *CreateIn } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (ci *CreateIndexes) Crypt(crypt *driver.Crypt) *CreateIndexes { +func (ci *CreateIndexes) Crypt(crypt driver.Crypt) *CreateIndexes { if ci == nil { ci = new(CreateIndexes) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/createIndexes.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/createIndexes.toml deleted file mode 100644 index 43373030..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/createIndexes.toml +++ /dev/null @@ -1,42 +0,0 @@ -version = 0 -name = "CreateIndexes" -documentation = "CreateIndexes performs a createIndexes operation." - -[properties] -enabled = ["write concern"] - -[command] -name = "createIndexes" -parameter = "collection" - -[request.indexes] -type = "array" -constructor = true -documentation = "An array containing index specification documents for the indexes being created." - -[request.maxTimeMS] -type = "int64" -documentation = "MaxTimeMS specifies the maximum amount of time to allow the query to run." - -[request.commitQuorum] -type = "value" -minWireVersionRequired = 9 -documentation = """ -The number of data-bearing members of a replica set, including the primary, that must complete the index builds -successfully before the primary marks the indexes as ready. This should either be a string or int32 value. -""" - -[response] -name = "CreateIndexesResult" - -[response.field.createdCollectionAutomatically] -type = "boolean" -documentation = "If the collection was created automatically." - -[response.field.indexesBefore] -type = "int32" -documentation = "The number of indexes existing before this command." - -[response.field.indexesAfter] -type = "int32" -documentation = "The number of indexes existing after this command." diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/delete.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/delete.go index c221f8a3..beb893c7 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/delete.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/delete.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -29,7 +27,7 @@ type Delete struct { clock *session.ClusterClock collection string monitor *event.CommandMonitor - crypt *driver.Crypt + crypt driver.Crypt database string deployment driver.Deployment selector description.ServerSelector @@ -38,14 +36,16 @@ type Delete struct { hint *bool result DeleteResult serverAPI *driver.ServerAPIOptions + let bsoncore.Document } +// DeleteResult represents a delete result returned by the server. type DeleteResult struct { // Number of documents successfully deleted. - N int32 + N int64 } -func buildDeleteResult(response bsoncore.Document, srvr driver.Server) (DeleteResult, error) { +func buildDeleteResult(response bsoncore.Document) (DeleteResult, error) { elements, err := response.Elements() if err != nil { return DeleteResult{}, err @@ -55,9 +55,9 @@ func buildDeleteResult(response bsoncore.Document, srvr driver.Server) (DeleteRe switch element.Key() { case "n": var ok bool - dr.N, ok = element.Value().AsInt32OK() + dr.N, ok = element.Value().AsInt64OK() if !ok { - err = fmt.Errorf("response field 'n' is type int32, but received BSON type %s", element.Value().Type) + return dr, fmt.Errorf("response field 'n' is type int32 or int64, but received BSON type %s", element.Value().Type) } } } @@ -75,12 +75,12 @@ func NewDelete(deletes ...bsoncore.Document) *Delete { func (d *Delete) Result() DeleteResult { return d.result } func (d *Delete) processResponse(info driver.ResponseInfo) error { - dr, err := buildDeleteResult(info.ServerResponse, info.Server) + dr, err := buildDeleteResult(info.ServerResponse) d.result.N += dr.N return err } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (d *Delete) Execute(ctx context.Context) error { if d.deployment == nil { return errors.New("the Delete operation must have a Deployment set before Execute can be called") @@ -123,6 +123,9 @@ func (d *Delete) command(dst []byte, desc description.SelectedServer) ([]byte, e return nil, errUnacknowledgedHint } } + if d.let != nil { + dst = bsoncore.AppendDocumentElement(dst, "let", d.let) + } return dst, nil } @@ -190,7 +193,7 @@ func (d *Delete) CommandMonitor(monitor *event.CommandMonitor) *Delete { } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (d *Delete) Crypt(crypt *driver.Crypt) *Delete { +func (d *Delete) Crypt(crypt driver.Crypt) *Delete { if d == nil { d = new(Delete) } @@ -271,3 +274,13 @@ func (d *Delete) ServerAPI(serverAPI *driver.ServerAPIOptions) *Delete { d.serverAPI = serverAPI return d } + +// Let specifies the let document to use. This option is only valid for server versions 5.0 and above. +func (d *Delete) Let(let bsoncore.Document) *Delete { + if d == nil { + d = new(Delete) + } + + d.let = let + return d +} diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/delete.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/delete.toml deleted file mode 100644 index dea0a712..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/delete.toml +++ /dev/null @@ -1,47 +0,0 @@ -version = 0 -name = "Delete" -documentation = "Delete performs a delete operation" - -[properties] -enabled = ["write concern"] -retryable = {mode = "once per command", type = "writes"} -batches = "deletes" - -[command] -name = "delete" -parameter = "collection" - -[request.deletes] -type = "document" -slice = true -constructor = true -variadic = true -required = true -documentation = """ -Deletes adds documents to this operation that will be used to determine what documents to delete when this operation -is executed. These documents should have the form {q: , limit: , collation: }. The -collation field is optional. If limit is 0, there will be no limit on the number of documents deleted.\ -""" - -[request.ordered] -type = "boolean" -documentation = """ -Ordered sets ordered. If true, when a write fails, the operation will return the error, when -false write failures do not stop execution of the operation.\ -""" - -[request.hint] -type = "boolean" -minWireVersionRequired = 5 -documentation = """ -Hint is a flag to indicate that the update document contains a hint. Hint is only supported by -servers >= 4.4. Older servers >= 3.4 will report an error for using the hint option. For servers < -3.4, the driver will return an error if the hint option is used.\ -""" - -[response] -name = "DeleteResult" - -[response.field.n] -type = "int32" -documentation = "Number of documents successfully deleted." diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/distinct.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/distinct.go index a233b9af..4591c1ad 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/distinct.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/distinct.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -31,7 +29,7 @@ type Distinct struct { clock *session.ClusterClock collection string monitor *event.CommandMonitor - crypt *driver.Crypt + crypt driver.Crypt database string deployment driver.Deployment readConcern *readconcern.ReadConcern @@ -42,12 +40,13 @@ type Distinct struct { serverAPI *driver.ServerAPIOptions } +// DistinctResult represents a distinct result returned by the server. type DistinctResult struct { // The distinct values for the field. Values bsoncore.Value } -func buildDistinctResult(response bsoncore.Document, srvr driver.Server) (DistinctResult, error) { +func buildDistinctResult(response bsoncore.Document) (DistinctResult, error) { elements, err := response.Elements() if err != nil { return DistinctResult{}, err @@ -75,11 +74,11 @@ func (d *Distinct) Result() DistinctResult { return d.result } func (d *Distinct) processResponse(info driver.ResponseInfo) error { var err error - d.result, err = buildDistinctResult(info.ServerResponse, info.Server) + d.result, err = buildDistinctResult(info.ServerResponse) return err } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (d *Distinct) Execute(ctx context.Context) error { if d.deployment == nil { return errors.New("the Distinct operation must have a Deployment set before Execute can be called") @@ -205,7 +204,7 @@ func (d *Distinct) CommandMonitor(monitor *event.CommandMonitor) *Distinct { } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (d *Distinct) Crypt(crypt *driver.Crypt) *Distinct { +func (d *Distinct) Crypt(crypt driver.Crypt) *Distinct { if d == nil { d = new(Distinct) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/distinct.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/distinct.toml deleted file mode 100644 index 81849d56..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/distinct.toml +++ /dev/null @@ -1,37 +0,0 @@ -version = 0 -name = "Distinct" -documentation = "Distinct performs a distinct operation." - -[properties] -enabled = ["read concern", "read preference"] -retryable = {mode = "once per command", type = "reads"} - -[command] -name = "distinct" -parameter = "collection" - -[request.key] -type = "string" -constructor = true -documentation = "Key specifies which field to return distinct values for." - -[request.query] -type = "document" -constructor = true -documentation = "Query specifies which documents to return distinct values from." - -[request.maxTimeMS] -type = "int64" -documentation = "MaxTimeMS specifies the maximum amount of time to allow the query to run." - -[request.collation] -type = "document" -minWireVersionRequired = 5 -documentation = "Collation specifies a collation to be used." - -[response] -name = "DistinctResult" - -[response.field.values] -type = "value" -documentation = "The distinct values for the field." \ No newline at end of file diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_collection.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_collection.go index 769e486e..fab279dc 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_collection.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_collection.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -27,7 +25,7 @@ type DropCollection struct { clock *session.ClusterClock collection string monitor *event.CommandMonitor - crypt *driver.Crypt + crypt driver.Crypt database string deployment driver.Deployment selector description.ServerSelector @@ -36,6 +34,7 @@ type DropCollection struct { serverAPI *driver.ServerAPIOptions } +// DropCollectionResult represents a dropCollection result returned by the server. type DropCollectionResult struct { // The number of indexes in the dropped collection. NIndexesWas int32 @@ -43,7 +42,7 @@ type DropCollectionResult struct { Ns string } -func buildDropCollectionResult(response bsoncore.Document, srvr driver.Server) (DropCollectionResult, error) { +func buildDropCollectionResult(response bsoncore.Document) (DropCollectionResult, error) { elements, err := response.Elements() if err != nil { return DropCollectionResult{}, err @@ -55,13 +54,13 @@ func buildDropCollectionResult(response bsoncore.Document, srvr driver.Server) ( var ok bool dcr.NIndexesWas, ok = element.Value().AsInt32OK() if !ok { - err = fmt.Errorf("response field 'nIndexesWas' is type int32, but received BSON type %s", element.Value().Type) + return dcr, fmt.Errorf("response field 'nIndexesWas' is type int32, but received BSON type %s", element.Value().Type) } case "ns": var ok bool dcr.Ns, ok = element.Value().StringValueOK() if !ok { - err = fmt.Errorf("response field 'ns' is type string, but received BSON type %s", element.Value().Type) + return dcr, fmt.Errorf("response field 'ns' is type string, but received BSON type %s", element.Value().Type) } } } @@ -78,11 +77,11 @@ func (dc *DropCollection) Result() DropCollectionResult { return dc.result } func (dc *DropCollection) processResponse(info driver.ResponseInfo) error { var err error - dc.result, err = buildDropCollectionResult(info.ServerResponse, info.Server) + dc.result, err = buildDropCollectionResult(info.ServerResponse) return err } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (dc *DropCollection) Execute(ctx context.Context) error { if dc.deployment == nil { return errors.New("the DropCollection operation must have a Deployment set before Execute can be called") @@ -150,7 +149,7 @@ func (dc *DropCollection) CommandMonitor(monitor *event.CommandMonitor) *DropCol } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (dc *DropCollection) Crypt(crypt *driver.Crypt) *DropCollection { +func (dc *DropCollection) Crypt(crypt driver.Crypt) *DropCollection { if dc == nil { dc = new(DropCollection) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_collection.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_collection.toml deleted file mode 100644 index b98db33e..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_collection.toml +++ /dev/null @@ -1,21 +0,0 @@ -version = 0 -name = "DropCollection" -documentation = "DropCollection performs a drop operation." - -[command] -name = "drop" -parameter = "collection" - -[properties] -enabled = ["write concern"] - -[response] -name = "DropCollectionResult" - -[response.field.ns] -type = "string" -documentation = "The namespace of the dropped collection." - -[response.field.nIndexesWas] -type = "int32" -documentation = "The number of indexes in the dropped collection." diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_database.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_database.go index 47bf4f87..be2220c6 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_database.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_database.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -25,7 +23,7 @@ type DropDatabase struct { session *session.Client clock *session.ClusterClock monitor *event.CommandMonitor - crypt *driver.Crypt + crypt driver.Crypt database string deployment driver.Deployment selector description.ServerSelector @@ -38,7 +36,7 @@ func NewDropDatabase() *DropDatabase { return &DropDatabase{} } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (dd *DropDatabase) Execute(ctx context.Context) error { if dd.deployment == nil { return errors.New("the DropDatabase operation must have a Deployment set before Execute can be called") @@ -96,7 +94,7 @@ func (dd *DropDatabase) CommandMonitor(monitor *event.CommandMonitor) *DropDatab } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (dd *DropDatabase) Crypt(crypt *driver.Crypt) *DropDatabase { +func (dd *DropDatabase) Crypt(crypt driver.Crypt) *DropDatabase { if dd == nil { dd = new(DropDatabase) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_database.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_database.toml deleted file mode 100644 index 9c576ea0..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_database.toml +++ /dev/null @@ -1,18 +0,0 @@ -version = 0 -name = "DropDatabase" -documentation = "DropDatabase performs a dropDatabase operation" - -[properties] -enabled = ["write concern"] -disabled = ["collection"] - -[command] -name = "dropDatabase" -parameter = "database" - -[response] -name = "DropDatabaseResult" - -[response.field.dropped] -type = "string" -documentation = "The dropped database." \ No newline at end of file diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_indexes.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_indexes.go index fa53a0f0..127f3951 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_indexes.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_indexes.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -29,7 +27,7 @@ type DropIndexes struct { clock *session.ClusterClock collection string monitor *event.CommandMonitor - crypt *driver.Crypt + crypt driver.Crypt database string deployment driver.Deployment selector description.ServerSelector @@ -38,12 +36,13 @@ type DropIndexes struct { serverAPI *driver.ServerAPIOptions } +// DropIndexesResult represents a dropIndexes result returned by the server. type DropIndexesResult struct { // Number of indexes that existed before the drop was executed. NIndexesWas int32 } -func buildDropIndexesResult(response bsoncore.Document, srvr driver.Server) (DropIndexesResult, error) { +func buildDropIndexesResult(response bsoncore.Document) (DropIndexesResult, error) { elements, err := response.Elements() if err != nil { return DropIndexesResult{}, err @@ -55,7 +54,7 @@ func buildDropIndexesResult(response bsoncore.Document, srvr driver.Server) (Dro var ok bool dir.NIndexesWas, ok = element.Value().AsInt32OK() if !ok { - err = fmt.Errorf("response field 'nIndexesWas' is type int32, but received BSON type %s", element.Value().Type) + return dir, fmt.Errorf("response field 'nIndexesWas' is type int32, but received BSON type %s", element.Value().Type) } } } @@ -74,11 +73,11 @@ func (di *DropIndexes) Result() DropIndexesResult { return di.result } func (di *DropIndexes) processResponse(info driver.ResponseInfo) error { var err error - di.result, err = buildDropIndexesResult(info.ServerResponse, info.Server) + di.result, err = buildDropIndexesResult(info.ServerResponse) return err } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (di *DropIndexes) Execute(ctx context.Context) error { if di.deployment == nil { return errors.New("the DropIndexes operation must have a Deployment set before Execute can be called") @@ -173,7 +172,7 @@ func (di *DropIndexes) CommandMonitor(monitor *event.CommandMonitor) *DropIndexe } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (di *DropIndexes) Crypt(crypt *driver.Crypt) *DropIndexes { +func (di *DropIndexes) Crypt(crypt driver.Crypt) *DropIndexes { if di == nil { di = new(DropIndexes) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_indexes.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_indexes.toml deleted file mode 100644 index 8eafe7db..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/drop_indexes.toml +++ /dev/null @@ -1,28 +0,0 @@ -version = 0 -name = "DropIndexes" -documentation = "DropIndexes performs an dropIndexes operation." - -[properties] -enabled = ["write concern"] - -[command] -name = "dropIndexes" -parameter = "collection" - -[request.index] -type = "string" -constructor = true -documentation = """ -Index specifies the name of the index to drop. If '*' is specified, all indexes will be dropped. -""" - -[request.maxTimeMS] -type = "int64" -documentation = "MaxTimeMS specifies the maximum amount of time to allow the query to run." - -[response] -name = "DropIndexesResult" - -[response.field.nIndexesWas] -type = "int32" -documentation = "Number of indexes that existed before the drop was executed." diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/end_sessions.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/end_sessions.go index 94381801..8384fb2d 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/end_sessions.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/end_sessions.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -25,7 +23,7 @@ type EndSessions struct { session *session.Client clock *session.ClusterClock monitor *event.CommandMonitor - crypt *driver.Crypt + crypt driver.Crypt database string deployment driver.Deployment selector description.ServerSelector @@ -44,7 +42,7 @@ func (es *EndSessions) processResponse(driver.ResponseInfo) error { return err } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (es *EndSessions) Execute(ctx context.Context) error { if es.deployment == nil { return errors.New("the EndSessions operation must have a Deployment set before Execute can be called") @@ -72,7 +70,7 @@ func (es *EndSessions) command(dst []byte, desc description.SelectedServer) ([]b return dst, nil } -// sessionIDs specify the sessions to be expired. +// SessionIDs specifies the sessions to be expired. func (es *EndSessions) SessionIDs(sessionIDs bsoncore.Document) *EndSessions { if es == nil { es = new(EndSessions) @@ -113,7 +111,7 @@ func (es *EndSessions) CommandMonitor(monitor *event.CommandMonitor) *EndSession } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (es *EndSessions) Crypt(crypt *driver.Crypt) *EndSessions { +func (es *EndSessions) Crypt(crypt driver.Crypt) *EndSessions { if es == nil { es = new(EndSessions) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/end_sessions.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/end_sessions.toml deleted file mode 100644 index 92910548..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/end_sessions.toml +++ /dev/null @@ -1,16 +0,0 @@ -version = 0 -name = "EndSessions" -documentation = "EndSessions performs an endSessions operation." - -[properties] -disabled = ["collection"] - -[command] -name = "endSessions" -parameter = "sessionIDs" - -[request.sessionIDs] -type = "array" -documentation = "sessionIDs specify the sessions to be expired." -skip = true -constructor = true diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find.go index 8e3e0f1c..3689a34b 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -32,6 +30,7 @@ type Find struct { comment *string filter bsoncore.Document hint bsoncore.Value + let bsoncore.Document limit *int64 max bsoncore.Document maxTimeMS *int64 @@ -50,7 +49,7 @@ type Find struct { clock *session.ClusterClock collection string monitor *event.CommandMonitor - crypt *driver.Crypt + crypt driver.Crypt database string deployment driver.Deployment readConcern *readconcern.ReadConcern @@ -80,7 +79,7 @@ func (f *Find) processResponse(info driver.ResponseInfo) error { return err } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (f *Find) Execute(ctx context.Context) error { if f.deployment == nil { return errors.New("the Find operation must have a Deployment set before Execute can be called") @@ -138,6 +137,9 @@ func (f *Find) command(dst []byte, desc description.SelectedServer) ([]byte, err if f.hint.Type != bsontype.Type(0) { dst = bsoncore.AppendValueElement(dst, "hint", f.hint) } + if f.let != nil { + dst = bsoncore.AppendDocumentElement(dst, "let", f.let) + } if f.limit != nil { dst = bsoncore.AppendInt64Element(dst, "limit", *f.limit) } @@ -263,6 +265,16 @@ func (f *Find) Hint(hint bsoncore.Value) *Find { return f } +// Let specifies the let document to use. This option is only valid for server versions 5.0 and above. +func (f *Find) Let(let bsoncore.Document) *Find { + if f == nil { + f = new(Find) + } + + f.let = let + return f +} + // Limit sets a limit on the number of documents to return. func (f *Find) Limit(limit int64) *Find { if f == nil { @@ -323,7 +335,7 @@ func (f *Find) OplogReplay(oplogReplay bool) *Find { return f } -// Project limits the fields returned for all documents. +// Projection limits the fields returned for all documents. func (f *Find) Projection(projection bsoncore.Document) *Find { if f == nil { f = new(Find) @@ -444,7 +456,7 @@ func (f *Find) CommandMonitor(monitor *event.CommandMonitor) *Find { } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (f *Find) Crypt(crypt *driver.Crypt) *Find { +func (f *Find) Crypt(crypt driver.Crypt) *Find { if f == nil { f = new(Find) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find.toml deleted file mode 100644 index dd52fd3d..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find.toml +++ /dev/null @@ -1,105 +0,0 @@ -version = 0 -name = "Find" -documentation = "Find performs a find operation." -response.type = "batch cursor" - -[properties] -enabled = ["collection", "read concern", "read preference", "command monitor", "client session", "cluster clock"] -retryable = {mode = "once per command", type = "reads"} -legacy = "find" - -[command] -name = "find" -parameter = "collection" - -[request.filter] -type = "document" -constructor = true -documentation = "Filter determines what results are returned from find." - -[request.sort] -type = "document" -documentation = "Sort specifies the order in which to return results." - -[request.projection] -type = "document" -documentation = "Project limits the fields returned for all documents." - -[request.hint] -type = "value" -documentation = "Hint specifies the index to use." - -[request.skip] -type = "int64" -documentation = "Skip specifies the number of documents to skip before returning." - -[request.limit] -type = "int64" -documentation = "Limit sets a limit on the number of documents to return." - -[request.batchSize] -type = "int32" -documentation = "BatchSize specifies the number of documents to return in every batch." - -[request.singleBatch] -type = "boolean" -documentation = "SingleBatch specifies whether the results should be returned in a single batch." - -[request.comment] -type = "string" -documentation = "Comment sets a string to help trace an operation." - -[request.maxTimeMS] -type = "int64" -documentation = "MaxTimeMS specifies the maximum amount of time to allow the query to run." - -[request.max] -type = "document" -documentation = "Max sets an exclusive upper bound for a specific index." - -[request.min] -type = "document" -documentation = "Min sets an inclusive lower bound for a specific index." - -[request.returnKey] -type = "boolean" -documentation = "ReturnKey when true returns index keys for all result documents." - -[request.showRecordID] -type = "boolean" -documentation = "ShowRecordID when true adds a $recordId field with the record identifier to returned documents." -keyName = "showRecordId" - -[request.oplogReplay] -type = "boolean" -documentation = "OplogReplay when true replays a replica set's oplog." - -[request.noCursorTimeout] -type = "boolean" -documentation = "NoCursorTimeout when true prevents cursor from timing out after an inactivity period." - -[request.tailable] -type = "boolean" -documentation = "Tailable keeps a cursor open and resumable after the last data has been retrieved." - -[request.awaitData] -type = "boolean" -documentation = "AwaitData when true makes a cursor block before returning when no data is available." - -[request.allowPartialResults] -type = "boolean" -documentation = "AllowPartialResults when true allows partial results to be returned if some shards are down." - -[request.allowDiskUse] -type = "boolean" -minWireVersionRequired = 4 -documentation = "AllowDiskUse when true allows temporary data to be written to disk during the find command." - -[request.collation] -type = "document" -minWireVersionRequired = 5 -documentation = "Collation specifies a collation to be used." - -[request.snapshot] -type = "boolean" -documentation = "Snapshot prevents the cursor from returning a document more than once because of an intervening write operation." diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find_and_modify.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find_and_modify.go index d1ad66f0..3e09ca44 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find_and_modify.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find_and_modify.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -45,13 +43,15 @@ type FindAndModify struct { selector description.ServerSelector writeConcern *writeconcern.WriteConcern retry *driver.RetryMode - crypt *driver.Crypt + crypt driver.Crypt hint bsoncore.Value serverAPI *driver.ServerAPIOptions + let bsoncore.Document result FindAndModifyResult } +// LastErrorObject represents information about updates and upserts returned by the server. type LastErrorObject struct { // True if an update modified an existing document UpdatedExisting bool @@ -59,6 +59,7 @@ type LastErrorObject struct { Upserted interface{} } +// FindAndModifyResult represents a findAndModify result returned by the server. type FindAndModifyResult struct { // Either the old or modified document, depending on the value of the new parameter. Value bsoncore.Document @@ -66,7 +67,7 @@ type FindAndModifyResult struct { LastErrorObject LastErrorObject } -func buildFindAndModifyResult(response bsoncore.Document, srvr driver.Server) (FindAndModifyResult, error) { +func buildFindAndModifyResult(response bsoncore.Document) (FindAndModifyResult, error) { elements, err := response.Elements() if err != nil { return FindAndModifyResult{}, err @@ -74,23 +75,23 @@ func buildFindAndModifyResult(response bsoncore.Document, srvr driver.Server) (F famr := FindAndModifyResult{} for _, element := range elements { switch element.Key() { - case "value": var ok bool famr.Value, ok = element.Value().DocumentOK() - if !ok { - err = fmt.Errorf("response field 'value' is type document, but received BSON type %s", element.Value().Type) + + // The 'value' field returned by a FindAndModify can be null in the case that no document was found. + if element.Value().Type != bsontype.Null && !ok { + return famr, fmt.Errorf("response field 'value' is type document or null, but received BSON type %s", element.Value().Type) } case "lastErrorObject": valDoc, ok := element.Value().DocumentOK() if !ok { - err = fmt.Errorf("response field 'lastErrorObject' is type document, but received BSON type %s", element.Value().Type) - break + return famr, fmt.Errorf("response field 'lastErrorObject' is type document, but received BSON type %s", element.Value().Type) } var leo LastErrorObject if err = bson.Unmarshal(valDoc, &leo); err != nil { - break + return famr, err } famr.LastErrorObject = leo } @@ -111,12 +112,12 @@ func (fam *FindAndModify) Result() FindAndModifyResult { return fam.result } func (fam *FindAndModify) processResponse(info driver.ResponseInfo) error { var err error - fam.result, err = buildFindAndModifyResult(info.ServerResponse, info.Server) + fam.result, err = buildFindAndModifyResult(info.ServerResponse) return err } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (fam *FindAndModify) Execute(ctx context.Context) error { if fam.deployment == nil { return errors.New("the FindAndModify operation must have a Deployment set before Execute can be called") @@ -202,6 +203,9 @@ func (fam *FindAndModify) command(dst []byte, desc description.SelectedServer) ( } dst = bsoncore.AppendValueElement(dst, "hint", fam.hint) } + if fam.let != nil { + dst = bsoncore.AppendDocumentElement(dst, "let", fam.let) + } return dst, nil } @@ -410,7 +414,7 @@ func (fam *FindAndModify) Retry(retry driver.RetryMode) *FindAndModify { } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (fam *FindAndModify) Crypt(crypt *driver.Crypt) *FindAndModify { +func (fam *FindAndModify) Crypt(crypt driver.Crypt) *FindAndModify { if fam == nil { fam = new(FindAndModify) } @@ -438,3 +442,13 @@ func (fam *FindAndModify) ServerAPI(serverAPI *driver.ServerAPIOptions) *FindAnd fam.serverAPI = serverAPI return fam } + +// Let specifies the let document to use. This option is only valid for server versions 5.0 and above. +func (fam *FindAndModify) Let(let bsoncore.Document) *FindAndModify { + if fam == nil { + fam = new(FindAndModify) + } + + fam.let = let + return fam +} diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find_and_modify.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find_and_modify.toml deleted file mode 100644 index 4f5edf26..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/find_and_modify.toml +++ /dev/null @@ -1,73 +0,0 @@ -version = 0 -name = "FindAndModify" -documentation = "FindAndModify performs a findAndModify operation." - -[properties] -enabled = ["write concern"] -retryable = {mode = "once", type = "writes"} - -[command] -name = "findAndModify" -parameter = "collection" - -[request.query] -type = "document" -constructor = true -documentation = "Query specifies the selection criteria for the modification." - -[request.sort] -type = "document" -documentation = """ -Sort determines which document the operation modifies if the query matches multiple documents.\ -The first document matched by the sort order will be modified. -""" - -[request.remove] -type = "boolean" -documentation = "Remove specifies that the matched document should be removed. Defaults to false." - -[request.update] -type = "value" -documentation = "Update specifies the update document to perform on the matched document." - -[request.newDocument] -type = "boolean" -documentation = "NewDocument specifies whether to return the modified document or the original. Defaults to false (return original)." - -[request.fields] -type = "document" -documentation = "Fields specifies a subset of fields to return." - -[request.upsert] -type = "boolean" -documentation = "Upsert specifies whether or not to create a new document if no documents match the query when doing an update. Defaults to false." - -[request.bypassDocumentValidation] -type = "boolean" -documentation = "BypassDocumentValidation specifies if document validation can be skipped when executing the operation." - -[request.maxTimeMS] -type = "int64" -documentation = "MaxTimeMS specifies the maximum amount of time to allow the operation to run." - -[request.collation] -type = "document" -minWireVersionRequired = 5 -documentation = "Collation specifies a collation to be used." - -[request.arrayFilters] -type = "array" -minWireVersionRequired = 6 -documentation = "ArrayFilters specifies an array of filter documents that determines which array elements to modify for an update operation on an array field." - -[request.hint] -type = "value" -minWireVersionRequired = 8 -documentation = "Hint specifies the index to use. This option is only valid for server versions >= 4.4. Server version 4.2 will error if this option is set. For server versions < 4.2, the driver will error if this option is set." - -[response] -name = "FindAndModifyResult" - -[response.field.value] -type = "document" -documentation = "Either the old or modified document, depending on the value of the new parameter." diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/ismaster.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/hello.go similarity index 50% rename from vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/ismaster.go rename to vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/hello.go index 544ed4e5..5e75c08e 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/ismaster.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/hello.go @@ -1,9 +1,14 @@ +// Copyright (C) MongoDB, Inc. 2021-present. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + package operation import ( "context" "errors" - "fmt" "runtime" "strconv" @@ -17,8 +22,8 @@ import ( "go.mongodb.org/mongo-driver/x/mongo/driver/session" ) -// IsMaster is used to run the isMaster handshake operation. -type IsMaster struct { +// Hello is used to run the handshake operation. +type Hello struct { appname string compressors []string saslSupportedMechs string @@ -33,138 +38,97 @@ type IsMaster struct { res bsoncore.Document } -var _ driver.Handshaker = (*IsMaster)(nil) +var _ driver.Handshaker = (*Hello)(nil) -// NewIsMaster constructs an IsMaster. -func NewIsMaster() *IsMaster { return &IsMaster{} } +// NewHello constructs a Hello. +func NewHello() *Hello { return &Hello{} } // AppName sets the application name in the client metadata sent in this operation. -func (im *IsMaster) AppName(appname string) *IsMaster { - im.appname = appname - return im +func (h *Hello) AppName(appname string) *Hello { + h.appname = appname + return h } // ClusterClock sets the cluster clock for this operation. -func (im *IsMaster) ClusterClock(clock *session.ClusterClock) *IsMaster { - if im == nil { - im = new(IsMaster) +func (h *Hello) ClusterClock(clock *session.ClusterClock) *Hello { + if h == nil { + h = new(Hello) } - im.clock = clock - return im + h.clock = clock + return h } // Compressors sets the compressors that can be used. -func (im *IsMaster) Compressors(compressors []string) *IsMaster { - im.compressors = compressors - return im +func (h *Hello) Compressors(compressors []string) *Hello { + h.compressors = compressors + return h } // SASLSupportedMechs retrieves the supported SASL mechanism for the given user when this operation // is run. -func (im *IsMaster) SASLSupportedMechs(username string) *IsMaster { - im.saslSupportedMechs = username - return im +func (h *Hello) SASLSupportedMechs(username string) *Hello { + h.saslSupportedMechs = username + return h } // Deployment sets the Deployment for this operation. -func (im *IsMaster) Deployment(d driver.Deployment) *IsMaster { - im.d = d - return im +func (h *Hello) Deployment(d driver.Deployment) *Hello { + h.d = d + return h } // SpeculativeAuthenticate sets the document to be used for speculative authentication. -func (im *IsMaster) SpeculativeAuthenticate(doc bsoncore.Document) *IsMaster { - im.speculativeAuth = doc - return im +func (h *Hello) SpeculativeAuthenticate(doc bsoncore.Document) *Hello { + h.speculativeAuth = doc + return h } // TopologyVersion sets the TopologyVersion to be used for heartbeats. -func (im *IsMaster) TopologyVersion(tv *description.TopologyVersion) *IsMaster { - im.topologyVersion = tv - return im +func (h *Hello) TopologyVersion(tv *description.TopologyVersion) *Hello { + h.topologyVersion = tv + return h } -// MaxAwaitTimeMS sets the maximum time for the sever to wait for topology changes during a heartbeat. -func (im *IsMaster) MaxAwaitTimeMS(awaitTime int64) *IsMaster { - im.maxAwaitTimeMS = &awaitTime - return im +// MaxAwaitTimeMS sets the maximum time for the server to wait for topology changes during a heartbeat. +func (h *Hello) MaxAwaitTimeMS(awaitTime int64) *Hello { + h.maxAwaitTimeMS = &awaitTime + return h } // ServerAPI sets the server API version for this operation. -func (im *IsMaster) ServerAPI(serverAPI *driver.ServerAPIOptions) *IsMaster { - im.serverAPI = serverAPI - return im +func (h *Hello) ServerAPI(serverAPI *driver.ServerAPIOptions) *Hello { + h.serverAPI = serverAPI + return h } // LoadBalanced specifies whether or not this operation is being sent over a connection to a load balanced cluster. -func (im *IsMaster) LoadBalanced(lb bool) *IsMaster { - im.loadBalanced = lb - return im +func (h *Hello) LoadBalanced(lb bool) *Hello { + h.loadBalanced = lb + return h } // Result returns the result of executing this operation. -func (im *IsMaster) Result(addr address.Address) description.Server { - return description.NewServer(addr, bson.Raw(im.res)) -} - -func (im *IsMaster) decodeStringSlice(element bsoncore.Element, name string) ([]string, error) { - arr, ok := element.Value().ArrayOK() - if !ok { - return nil, fmt.Errorf("expected '%s' to be an array but it's a BSON %s", name, element.Value().Type) - } - vals, err := arr.Values() - if err != nil { - return nil, err - } - var strs []string - for _, val := range vals { - str, ok := val.StringValueOK() - if !ok { - return nil, fmt.Errorf("expected '%s' to be an array of strings, but found a BSON %s", name, val.Type) - } - strs = append(strs, str) - } - return strs, nil -} - -func (im *IsMaster) decodeStringMap(element bsoncore.Element, name string) (map[string]string, error) { - doc, ok := element.Value().DocumentOK() - if !ok { - return nil, fmt.Errorf("expected '%s' to be a document but it's a BSON %s", name, element.Value().Type) - } - elements, err := doc.Elements() - if err != nil { - return nil, err - } - m := make(map[string]string) - for _, element := range elements { - key := element.Key() - value, ok := element.Value().StringValueOK() - if !ok { - return nil, fmt.Errorf("expected '%s' to be a document of strings, but found a BSON %s", name, element.Value().Type) - } - m[key] = value - } - return m, nil +func (h *Hello) Result(addr address.Address) description.Server { + return description.NewServer(addr, bson.Raw(h.res)) } // handshakeCommand appends all necessary command fields as well as client metadata, SASL supported mechs, and compression. -func (im *IsMaster) handshakeCommand(dst []byte, desc description.SelectedServer) ([]byte, error) { - dst, err := im.command(dst, desc) +func (h *Hello) handshakeCommand(dst []byte, desc description.SelectedServer) ([]byte, error) { + dst, err := h.command(dst, desc) if err != nil { return dst, err } - if im.saslSupportedMechs != "" { - dst = bsoncore.AppendStringElement(dst, "saslSupportedMechs", im.saslSupportedMechs) + if h.saslSupportedMechs != "" { + dst = bsoncore.AppendStringElement(dst, "saslSupportedMechs", h.saslSupportedMechs) } - if im.speculativeAuth != nil { - dst = bsoncore.AppendDocumentElement(dst, "speculativeAuthenticate", im.speculativeAuth) + if h.speculativeAuth != nil { + dst = bsoncore.AppendDocumentElement(dst, "speculativeAuthenticate", h.speculativeAuth) } var idx int32 idx, dst = bsoncore.AppendArrayElementStart(dst, "compression") - for i, compressor := range im.compressors { + for i, compressor := range h.compressors { dst = bsoncore.AppendStringElement(dst, strconv.Itoa(i), compressor) } dst, _ = bsoncore.AppendArrayEnd(dst, idx) @@ -183,9 +147,9 @@ func (im *IsMaster) handshakeCommand(dst []byte, desc description.SelectedServer dst, _ = bsoncore.AppendDocumentEnd(dst, didx) dst = bsoncore.AppendStringElement(dst, "platform", runtime.Version()) - if im.appname != "" { + if h.appname != "" { didx, dst = bsoncore.AppendDocumentElementStart(dst, "application") - dst = bsoncore.AppendStringElement(dst, "name", im.appname) + dst = bsoncore.AppendStringElement(dst, "name", h.appname) dst, _ = bsoncore.AppendDocumentEnd(dst, didx) } dst, _ = bsoncore.AppendDocumentEnd(dst, idx) @@ -194,14 +158,17 @@ func (im *IsMaster) handshakeCommand(dst []byte, desc description.SelectedServer } // command appends all necessary command fields. -func (im *IsMaster) command(dst []byte, _ description.SelectedServer) ([]byte, error) { - if im.serverAPI != nil { +func (h *Hello) command(dst []byte, desc description.SelectedServer) ([]byte, error) { + // Use "hello" if topology is LoadBalanced, API version is declared or server + // has responded with "helloOk". Otherwise, use legacy hello. + if desc.Kind == description.LoadBalanced || h.serverAPI != nil || desc.Server.HelloOK { dst = bsoncore.AppendInt32Element(dst, "hello", 1) } else { - dst = bsoncore.AppendInt32Element(dst, "isMaster", 1) + dst = bsoncore.AppendInt32Element(dst, internal.LegacyHello, 1) } + dst = bsoncore.AppendBooleanElement(dst, "helloOk", true) - if tv := im.topologyVersion; tv != nil { + if tv := h.topologyVersion; tv != nil { var tvIdx int32 tvIdx, dst = bsoncore.AppendDocumentElementStart(dst, "topologyVersion") @@ -209,10 +176,10 @@ func (im *IsMaster) command(dst []byte, _ description.SelectedServer) ([]byte, e dst = bsoncore.AppendInt64Element(dst, "counter", tv.Counter) dst, _ = bsoncore.AppendDocumentEnd(dst, tvIdx) } - if im.maxAwaitTimeMS != nil { - dst = bsoncore.AppendInt64Element(dst, "maxAwaitTimeMS", *im.maxAwaitTimeMS) + if h.maxAwaitTimeMS != nil { + dst = bsoncore.AppendInt64Element(dst, "maxAwaitTimeMS", *h.maxAwaitTimeMS) } - if im.loadBalanced { + if h.loadBalanced { // The loadBalanced parameter should only be added if it's true. We should never explicitly send // loadBalanced=false per the load balancing spec. dst = bsoncore.AppendBooleanElement(dst, "loadBalanced", true) @@ -222,67 +189,70 @@ func (im *IsMaster) command(dst []byte, _ description.SelectedServer) ([]byte, e } // Execute runs this operation. -func (im *IsMaster) Execute(ctx context.Context) error { - if im.d == nil { - return errors.New("an IsMaster must have a Deployment set before Execute can be called") +func (h *Hello) Execute(ctx context.Context) error { + if h.d == nil { + return errors.New("a Hello must have a Deployment set before Execute can be called") } - return im.createOperation().Execute(ctx, nil) + return h.createOperation().Execute(ctx, nil) } -// StreamResponse gets the next streaming isMaster response from the server. -func (im *IsMaster) StreamResponse(ctx context.Context, conn driver.StreamerConnection) error { - return im.createOperation().ExecuteExhaust(ctx, conn, nil) +// StreamResponse gets the next streaming Hello response from the server. +func (h *Hello) StreamResponse(ctx context.Context, conn driver.StreamerConnection) error { + return h.createOperation().ExecuteExhaust(ctx, conn, nil) } -func (im *IsMaster) createOperation() driver.Operation { +func (h *Hello) createOperation() driver.Operation { return driver.Operation{ - Clock: im.clock, - CommandFn: im.command, + Clock: h.clock, + CommandFn: h.command, Database: "admin", - Deployment: im.d, + Deployment: h.d, ProcessResponseFn: func(info driver.ResponseInfo) error { - im.res = info.ServerResponse + h.res = info.ServerResponse return nil }, - ServerAPI: im.serverAPI, + ServerAPI: h.serverAPI, } } // GetHandshakeInformation performs the MongoDB handshake for the provided connection and returns the relevant // information about the server. This function implements the driver.Handshaker interface. -func (im *IsMaster) GetHandshakeInformation(ctx context.Context, _ address.Address, c driver.Connection) (driver.HandshakeInformation, error) { +func (h *Hello) GetHandshakeInformation(ctx context.Context, _ address.Address, c driver.Connection) (driver.HandshakeInformation, error) { err := driver.Operation{ - Clock: im.clock, - CommandFn: im.handshakeCommand, + Clock: h.clock, + CommandFn: h.handshakeCommand, Deployment: driver.SingleConnectionDeployment{c}, Database: "admin", ProcessResponseFn: func(info driver.ResponseInfo) error { - im.res = info.ServerResponse + h.res = info.ServerResponse return nil }, - ServerAPI: im.serverAPI, + ServerAPI: h.serverAPI, }.Execute(ctx, nil) if err != nil { return driver.HandshakeInformation{}, err } info := driver.HandshakeInformation{ - Description: im.Result(c.Address()), + Description: h.Result(c.Address()), } - if speculativeAuthenticate, ok := im.res.Lookup("speculativeAuthenticate").DocumentOK(); ok { + if speculativeAuthenticate, ok := h.res.Lookup("speculativeAuthenticate").DocumentOK(); ok { info.SpeculativeAuthenticate = speculativeAuthenticate } + if serverConnectionID, ok := h.res.Lookup("connectionId").Int32OK(); ok { + info.ServerConnectionID = &serverConnectionID + } // Cast to bson.Raw to lookup saslSupportedMechs to avoid converting from bsoncore.Value to bson.RawValue for the // StringSliceFromRawValue call. - if saslSupportedMechs, lookupErr := bson.Raw(im.res).LookupErr("saslSupportedMechs"); lookupErr == nil { + if saslSupportedMechs, lookupErr := bson.Raw(h.res).LookupErr("saslSupportedMechs"); lookupErr == nil { info.SaslSupportedMechs, err = internal.StringSliceFromRawValue("saslSupportedMechs", saslSupportedMechs) } return info, err } // FinishHandshake implements the Handshaker interface. This is a no-op function because a non-authenticated connection -// does not do anything besides the initial isMaster for a handshake. -func (im *IsMaster) FinishHandshake(context.Context, driver.Connection) error { +// does not do anything besides the initial Hello for a handshake. +func (h *Hello) FinishHandshake(context.Context, driver.Connection) error { return nil } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/insert.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/insert.go index b4d09537..993eac10 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/insert.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/insert.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -30,7 +28,7 @@ type Insert struct { clock *session.ClusterClock collection string monitor *event.CommandMonitor - crypt *driver.Crypt + crypt driver.Crypt database string deployment driver.Deployment selector description.ServerSelector @@ -40,12 +38,13 @@ type Insert struct { serverAPI *driver.ServerAPIOptions } +// InsertResult represents an insert result returned by the server. type InsertResult struct { // Number of documents successfully inserted. - N int32 + N int64 } -func buildInsertResult(response bsoncore.Document, srvr driver.Server) (InsertResult, error) { +func buildInsertResult(response bsoncore.Document) (InsertResult, error) { elements, err := response.Elements() if err != nil { return InsertResult{}, err @@ -55,9 +54,9 @@ func buildInsertResult(response bsoncore.Document, srvr driver.Server) (InsertRe switch element.Key() { case "n": var ok bool - ir.N, ok = element.Value().AsInt32OK() + ir.N, ok = element.Value().AsInt64OK() if !ok { - err = fmt.Errorf("response field 'n' is type int32, but received BSON type %s", element.Value().Type) + return ir, fmt.Errorf("response field 'n' is type int32 or int64, but received BSON type %s", element.Value().Type) } } } @@ -75,12 +74,12 @@ func NewInsert(documents ...bsoncore.Document) *Insert { func (i *Insert) Result() InsertResult { return i.result } func (i *Insert) processResponse(info driver.ResponseInfo) error { - ir, err := buildInsertResult(info.ServerResponse, info.Server) + ir, err := buildInsertResult(info.ServerResponse) i.result.N += ir.N return err } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (i *Insert) Execute(ctx context.Context) error { if i.deployment == nil { return errors.New("the Insert operation must have a Deployment set before Execute can be called") @@ -195,7 +194,7 @@ func (i *Insert) CommandMonitor(monitor *event.CommandMonitor) *Insert { } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (i *Insert) Crypt(crypt *driver.Crypt) *Insert { +func (i *Insert) Crypt(crypt driver.Crypt) *Insert { if i == nil { i = new(Insert) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/insert.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/insert.toml deleted file mode 100644 index 24fdd51c..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/insert.toml +++ /dev/null @@ -1,45 +0,0 @@ -version = 0 -name = "Insert" -documentation = "Insert performs an insert operation." - -[properties] -enabled = ["write concern"] -retryable = {mode = "once per command", type = "writes"} -batches = "documents" - -[command] -name = "insert" -parameter = "collection" - -[request.documents] -type = "document" -slice = true -constructor = true -variadic = true -required = true -documentation = """ -Documents adds documents to this operation that will be inserted when this operation is -executed.\ -""" - -[request.ordered] -type = "boolean" -documentation = """ -Ordered sets ordered. If true, when a write fails, the operation will return the error, when -false write failures do not stop execution of the operation.\ -""" - -[request.bypassDocumentValidation] -type = "boolean" -minWireVersion = 4 -documentation = """ -BypassDocumentValidation allows the operation to opt-out of document level validation. Valid -for server versions >= 3.2. For servers < 3.2, this setting is ignored.\ -""" - -[response] -name = "InsertResult" - -[response.field.n] -type = "int32" -documentation = "Number of documents successfully inserted." diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/listDatabases.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/listDatabases.go index 067c84ae..6b469b6f 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/listDatabases.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/listDatabases.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -35,12 +33,13 @@ type ListDatabases struct { readPreference *readpref.ReadPref retry *driver.RetryMode selector description.ServerSelector - crypt *driver.Crypt + crypt driver.Crypt serverAPI *driver.ServerAPIOptions result ListDatabasesResult } +// ListDatabasesResult represents a listDatabases result returned by the server. type ListDatabasesResult struct { // An array of documents, one document for each database Databases []databaseRecord @@ -54,7 +53,7 @@ type databaseRecord struct { Empty bool } -func buildListDatabasesResult(response bsoncore.Document, srvr driver.Server) (ListDatabasesResult, error) { +func buildListDatabasesResult(response bsoncore.Document) (ListDatabasesResult, error) { elements, err := response.Elements() if err != nil { return ListDatabasesResult{}, err @@ -62,76 +61,64 @@ func buildListDatabasesResult(response bsoncore.Document, srvr driver.Server) (L ir := ListDatabasesResult{} for _, element := range elements { switch element.Key() { - case "totalSize": var ok bool ir.TotalSize, ok = element.Value().AsInt64OK() if !ok { - err = fmt.Errorf("response field 'totalSize' is type int64, but received BSON type %s: %s", element.Value().Type, element.Value()) + return ir, fmt.Errorf("response field 'totalSize' is type int64, but received BSON type %s: %s", element.Value().Type, element.Value()) } - case "databases": - // TODO: Make operationgen handle array results. arr, ok := element.Value().ArrayOK() if !ok { - err = fmt.Errorf("response field 'databases' is type array, but received BSON type %s", element.Value().Type) - continue + return ir, fmt.Errorf("response field 'databases' is type array, but received BSON type %s", element.Value().Type) } var tmp bsoncore.Document - marshalErr := bson.Unmarshal(arr, &tmp) - if marshalErr != nil { - err = marshalErr - continue + err := bson.Unmarshal(arr, &tmp) + if err != nil { + return ir, err } - records, marshalErr := tmp.Elements() - if marshalErr != nil { - err = marshalErr - continue + + records, err := tmp.Elements() + if err != nil { + return ir, err } ir.Databases = make([]databaseRecord, len(records)) for i, val := range records { valueDoc, ok := val.Value().DocumentOK() if !ok { - err = fmt.Errorf("'databases' element is type document, but received BSON type %s", val.Value().Type) - continue + return ir, fmt.Errorf("'databases' element is type document, but received BSON type %s", val.Value().Type) } - elems, marshalErr := valueDoc.Elements() - if marshalErr != nil { - err = marshalErr - continue + elems, err := valueDoc.Elements() + if err != nil { + return ir, err } + for _, elem := range elems { switch elem.Key() { - case "name": ir.Databases[i].Name, ok = elem.Value().StringValueOK() if !ok { - err = fmt.Errorf("response field 'name' is type string, but received BSON type %s", elem.Value().Type) - continue + return ir, fmt.Errorf("response field 'name' is type string, but received BSON type %s", elem.Value().Type) } - case "sizeOnDisk": ir.Databases[i].SizeOnDisk, ok = elem.Value().AsInt64OK() if !ok { - err = fmt.Errorf("response field 'sizeOnDisk' is type int64, but received BSON type %s", elem.Value().Type) - continue + return ir, fmt.Errorf("response field 'sizeOnDisk' is type int64, but received BSON type %s", elem.Value().Type) } - case "empty": ir.Databases[i].Empty, ok = elem.Value().BooleanOK() if !ok { - err = fmt.Errorf("response field 'empty' is type bool, but received BSON type %s", elem.Value().Type) - continue + return ir, fmt.Errorf("response field 'empty' is type bool, but received BSON type %s", elem.Value().Type) } } } } } } - return ir, err + return ir, nil } // NewListDatabases constructs and returns a new ListDatabases. @@ -147,12 +134,12 @@ func (ld *ListDatabases) Result() ListDatabasesResult { return ld.result } func (ld *ListDatabases) processResponse(info driver.ResponseInfo) error { var err error - ld.result, err = buildListDatabasesResult(info.ServerResponse, info.Server) + ld.result, err = buildListDatabasesResult(info.ServerResponse) return err } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (ld *ListDatabases) Execute(ctx context.Context) error { if ld.deployment == nil { return errors.New("the ListDatabases operation must have a Deployment set before Execute can be called") @@ -307,7 +294,7 @@ func (ld *ListDatabases) Retry(retry driver.RetryMode) *ListDatabases { } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (ld *ListDatabases) Crypt(crypt *driver.Crypt) *ListDatabases { +func (ld *ListDatabases) Crypt(crypt driver.Crypt) *ListDatabases { if ld == nil { ld = new(ListDatabases) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/listDatabases.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/listDatabases.toml deleted file mode 100644 index 29d5a021..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/listDatabases.toml +++ /dev/null @@ -1,37 +0,0 @@ -version = 0 -name = "ListDatabases" -documentation = "ListDatabases performs a listDatabases operation." - -[properties] -enabled = ["read preference"] -retryable = {mode = "once per command", type = "reads"} -disabled = ["collection"] - -[command] -name = "listDatabases" -parameter = "database" - -[request.filter] -type = "document" -constructor = true -documentation = "Filter determines what results are returned from listDatabases." - - -[request.nameOnly] -type = "boolean" -documentation = "NameOnly specifies whether to only return database names." - -[request.authorizedDatabases] -type = "boolean" -documentation = "AuthorizedDatabases specifies whether to only return databases which the user is authorized to use." - -[response] -name = "ListDatabasesResult" - -[response.field.totalSize] -type = "int64" -documentation = "The sum of the size of all the database files on disk in bytes." - -[response.field.databases] -type = "value" -documentation = "An array of documents, one document for each database" diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_collections.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_collections.go index 8f96eb13..de30afd3 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_collections.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_collections.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -22,20 +20,21 @@ import ( // ListCollections performs a listCollections operation. type ListCollections struct { - filter bsoncore.Document - nameOnly *bool - session *session.Client - clock *session.ClusterClock - monitor *event.CommandMonitor - crypt *driver.Crypt - database string - deployment driver.Deployment - readPreference *readpref.ReadPref - selector description.ServerSelector - retry *driver.RetryMode - result driver.CursorResponse - batchSize *int32 - serverAPI *driver.ServerAPIOptions + filter bsoncore.Document + nameOnly *bool + authorizedCollections *bool + session *session.Client + clock *session.ClusterClock + monitor *event.CommandMonitor + crypt driver.Crypt + database string + deployment driver.Deployment + readPreference *readpref.ReadPref + selector description.ServerSelector + retry *driver.RetryMode + result driver.CursorResponse + batchSize *int32 + serverAPI *driver.ServerAPIOptions } // NewListCollections constructs and returns a new ListCollections. @@ -65,7 +64,7 @@ func (lc *ListCollections) processResponse(info driver.ResponseInfo) error { return err } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (lc *ListCollections) Execute(ctx context.Context) error { if lc.deployment == nil { return errors.New("the ListCollections operation must have a Deployment set before Execute can be called") @@ -91,7 +90,6 @@ func (lc *ListCollections) Execute(ctx context.Context) error { } func (lc *ListCollections) command(dst []byte, desc description.SelectedServer) ([]byte, error) { - dst = bsoncore.AppendInt32Element(dst, "listCollections", 1) if lc.filter != nil { dst = bsoncore.AppendDocumentElement(dst, "filter", lc.filter) @@ -99,6 +97,10 @@ func (lc *ListCollections) command(dst []byte, desc description.SelectedServer) if lc.nameOnly != nil { dst = bsoncore.AppendBooleanElement(dst, "nameOnly", *lc.nameOnly) } + if lc.authorizedCollections != nil { + dst = bsoncore.AppendBooleanElement(dst, "authorizedCollections", *lc.authorizedCollections) + } + cursorDoc := bsoncore.NewDocumentBuilder() if lc.batchSize != nil { cursorDoc.AppendInt32("batchSize", *lc.batchSize) @@ -128,6 +130,17 @@ func (lc *ListCollections) NameOnly(nameOnly bool) *ListCollections { return lc } +// AuthorizedCollections specifies whether to only return collections the user +// is authorized to use. +func (lc *ListCollections) AuthorizedCollections(authorizedCollections bool) *ListCollections { + if lc == nil { + lc = new(ListCollections) + } + + lc.authorizedCollections = &authorizedCollections + return lc +} + // Session sets the session for this operation. func (lc *ListCollections) Session(session *session.Client) *ListCollections { if lc == nil { @@ -159,7 +172,7 @@ func (lc *ListCollections) CommandMonitor(monitor *event.CommandMonitor) *ListCo } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (lc *ListCollections) Crypt(crypt *driver.Crypt) *ListCollections { +func (lc *ListCollections) Crypt(crypt driver.Crypt) *ListCollections { if lc == nil { lc = new(ListCollections) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_collections.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_collections.toml deleted file mode 100644 index 383dae11..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_collections.toml +++ /dev/null @@ -1,23 +0,0 @@ -version = 0 -name = "ListCollections" -documentation = "ListCollections performs a listCollections operation." -response.type = "list collections batch cursor" - -[properties] -enabled = ["read preference"] -disabled = ["collection"] -retryable = {mode = "once per command", type = "reads"} -legacy = "listCollections" - -[command] -name = "listCollections" -parameter = "database" - -[request.filter] -type = "document" -constructor = true -documentation = "Filter determines what results are returned from listCollections." - -[request.nameOnly] -type = "boolean" -documentation = "NameOnly specifies whether to only return collection names." diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_indexes.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_indexes.go index 33db54e4..171e2b5a 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_indexes.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_indexes.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// Code generated by operationgen. DO NOT EDIT. - package operation import ( @@ -31,7 +29,7 @@ type ListIndexes struct { deployment driver.Deployment selector description.ServerSelector retry *driver.RetryMode - crypt *driver.Crypt + crypt driver.Crypt serverAPI *driver.ServerAPIOptions result driver.CursorResponse @@ -60,7 +58,7 @@ func (li *ListIndexes) processResponse(info driver.ResponseInfo) error { } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (li *ListIndexes) Execute(ctx context.Context) error { if li.deployment == nil { return errors.New("the ListIndexes operation must have a Deployment set before Execute can be called") @@ -205,7 +203,7 @@ func (li *ListIndexes) Retry(retry driver.RetryMode) *ListIndexes { } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (li *ListIndexes) Crypt(crypt *driver.Crypt) *ListIndexes { +func (li *ListIndexes) Crypt(crypt driver.Crypt) *ListIndexes { if li == nil { li = new(ListIndexes) } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_indexes.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_indexes.toml deleted file mode 100644 index 98d0f886..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/list_indexes.toml +++ /dev/null @@ -1,20 +0,0 @@ -version = 0 -name = "ListIndexes" -documentation = "ListIndexes performs a listIndexes operation." -response.type = "batch cursor" - -[properties] -legacy = "listIndexes" -retryable = {mode = "once per command", type = "reads"} - -[command] -name = "listIndexes" -parameter = "collection" - -[request.batchSize] -type = "int32" -documentation = "BatchSize specifies the number of documents to return in every batch." - -[request.maxTimeMS] -type = "int64" -documentation = "MaxTimeMS specifies the maximum amount of time to allow the query to run." diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/operation.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/operation.go deleted file mode 100644 index 3a13b504..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/operation.go +++ /dev/null @@ -1,15 +0,0 @@ -package operation - -//go:generate operationgen insert.toml operation insert.go -//go:generate operationgen find.toml operation find.go -//go:generate operationgen list_collections.toml operation list_collections.go -//go:generate operationgen createIndexes.toml operation createIndexes.go -//go:generate operationgen drop_collection.toml operation drop_collection.go -//go:generate operationgen distinct.toml operation distinct.go -//go:generate operationgen drop_indexes.toml operation drop_indexes.go -//go:generate operationgen drop_database.toml operation drop_database.go -//go:generate operationgen commit_transaction.toml operation commit_transaction.go -//go:generate operationgen abort_transaction.toml operation abort_transaction.go -//go:generate operationgen count.toml operation count.go -//go:generate operationgen end_sessions.toml operation end_sessions.go -//go:generate operationgen create.toml operation create.go diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/update.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/update.go index 0a3a9e6d..3cd11848 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/update.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/update.go @@ -4,8 +4,6 @@ // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -// NOTE: This file is maintained by hand because operationgen cannot generate it. - package operation import ( @@ -39,8 +37,9 @@ type Update struct { writeConcern *writeconcern.WriteConcern retry *driver.RetryMode result UpdateResult - crypt *driver.Crypt + crypt driver.Crypt serverAPI *driver.ServerAPIOptions + let bsoncore.Document } // Upsert contains the information for an upsert in an Update operation. @@ -52,14 +51,14 @@ type Upsert struct { // UpdateResult contains information for the result of an Update operation. type UpdateResult struct { // Number of documents matched. - N int32 + N int64 // Number of documents modified. - NModified int32 + NModified int64 // Information about upserted documents. Upserted []Upsert } -func buildUpdateResult(response bsoncore.Document, srvr driver.Server) (UpdateResult, error) { +func buildUpdateResult(response bsoncore.Document) (UpdateResult, error) { elements, err := response.Elements() if err != nil { return UpdateResult{}, err @@ -67,26 +66,22 @@ func buildUpdateResult(response bsoncore.Document, srvr driver.Server) (UpdateRe ur := UpdateResult{} for _, element := range elements { switch element.Key() { - case "nModified": var ok bool - ur.NModified, ok = element.Value().Int32OK() + ur.NModified, ok = element.Value().AsInt64OK() if !ok { - err = fmt.Errorf("response field 'nModified' is type int32, but received BSON type %s", element.Value().Type) + return ur, fmt.Errorf("response field 'nModified' is type int32 or int64, but received BSON type %s", element.Value().Type) } - case "n": var ok bool - ur.N, ok = element.Value().Int32OK() + ur.N, ok = element.Value().AsInt64OK() if !ok { - err = fmt.Errorf("response field 'n' is type int32, but received BSON type %s", element.Value().Type) + return ur, fmt.Errorf("response field 'n' is type int32 or int64, but received BSON type %s", element.Value().Type) } - case "upserted": arr, ok := element.Value().ArrayOK() if !ok { - err = fmt.Errorf("response field 'upserted' is type array, but received BSON type %s", element.Value().Type) - break + return ur, fmt.Errorf("response field 'upserted' is type array, but received BSON type %s", element.Value().Type) } var values []bsoncore.Value @@ -98,12 +93,11 @@ func buildUpdateResult(response bsoncore.Document, srvr driver.Server) (UpdateRe for _, val := range values { valDoc, ok := val.DocumentOK() if !ok { - err = fmt.Errorf("upserted value is type document, but received BSON type %s", val.Type) - break + return ur, fmt.Errorf("upserted value is type document, but received BSON type %s", val.Type) } var upsert Upsert if err = bson.Unmarshal(valDoc, &upsert); err != nil { - break + return ur, err } ur.Upserted = append(ur.Upserted, upsert) } @@ -123,7 +117,7 @@ func NewUpdate(updates ...bsoncore.Document) *Update { func (u *Update) Result() UpdateResult { return u.result } func (u *Update) processResponse(info driver.ResponseInfo) error { - ur, err := buildUpdateResult(info.ServerResponse, info.Server) + ur, err := buildUpdateResult(info.ServerResponse) u.result.N += ur.N u.result.NModified += ur.NModified @@ -137,7 +131,7 @@ func (u *Update) processResponse(info driver.ResponseInfo) error { } -// Execute runs this operations and returns an error if the operaiton did not execute successfully. +// Execute runs this operations and returns an error if the operation did not execute successfully. func (u *Update) Execute(ctx context.Context) error { if u.deployment == nil { return errors.New("the Update operation must have a Deployment set before Execute can be called") @@ -192,6 +186,9 @@ func (u *Update) command(dst []byte, desc description.SelectedServer) ([]byte, e return nil, errors.New("the 'arrayFilters' command parameter requires a minimum server wire version of 6") } } + if u.let != nil { + dst = bsoncore.AppendDocumentElement(dst, "let", u.let) + } return dst, nil } @@ -346,7 +343,7 @@ func (u *Update) Retry(retry driver.RetryMode) *Update { } // Crypt sets the Crypt object to use for automatic encryption and decryption. -func (u *Update) Crypt(crypt *driver.Crypt) *Update { +func (u *Update) Crypt(crypt driver.Crypt) *Update { if u == nil { u = new(Update) } @@ -364,3 +361,13 @@ func (u *Update) ServerAPI(serverAPI *driver.ServerAPIOptions) *Update { u.serverAPI = serverAPI return u } + +// Let specifies the let document to use. This option is only valid for server versions 5.0 and above. +func (u *Update) Let(let bsoncore.Document) *Update { + if u == nil { + u = new(Update) + } + + u.let = let + return u +} diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/update.toml b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/update.toml deleted file mode 100644 index 6690c213..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation/update.toml +++ /dev/null @@ -1,58 +0,0 @@ -version = 0 -name = "Update" -documentation = "Update performs an update operation." - -[properties] -enabled = ["write concern"] -retryable = {mode = "once per command", type = "writes"} -batches = "updates" - -[command] -name = "update" -parameter = "collection" - -[request.updates] -type = "document" -slice = true -constructor = true -variadic = true -required = true -documentation = """ -Updates specifies an array of update statements to perform when this operation is executed. -Each update document must have the following structure: {q: , u: , multi: , collation: Optional, arrayFitlers: Optional, hint: Optional}.\ -""" - -[request.ordered] -type = "boolean" -documentation = """ -Ordered sets ordered. If true, when a write fails, the operation will return the error, when -false write failures do not stop execution of the operation.\ -""" - -[request.bypassDocumentValidation] -type = "boolean" -minWireVersion = 4 -documentation = """ -BypassDocumentValidation allows the operation to opt-out of document level validation. Valid -for server versions >= 3.2. For servers < 3.2, this setting is ignored.\ -""" - -[request.hint] -type = "boolean" -minWireVersionRequired = 5 -documentation = """ -Hint is a flag to indicate that the update document contains a hint. Hint is only supported by -servers >= 4.2. Older servers >= 3.4 will report an error for using the hint option. For servers < -3.4, the driver will return an error if the hint option is used.\ -""" - -[response] -name = "UpdateResult" - -[response.field.n] -type = "int32" -documentation = "Number of documents matched." - -[response.field.nModified] -type = "int32" -documentation = "Number of documents modified." diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation_legacy.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation_legacy.go index a4390d05..2584f484 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation_legacy.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation_legacy.go @@ -97,7 +97,7 @@ func (op Operation) createLegacyFindWireMessage(dst []byte, desc description.Sel // build options as a byte slice of elements rather than a bsoncore.Document because they will be appended // to another document with $query var optsElems []byte - flags := op.slaveOK(desc) + flags := op.secondaryOK(desc) var numToSkip, numToReturn, batchSize, limit int32 // numToReturn calculated from batchSize and limit var filter, returnFieldsSelector bsoncore.Document var collName string @@ -162,7 +162,7 @@ func (op Operation) createLegacyFindWireMessage(dst []byte, desc description.Sel numToReturn = op.calculateNumberToReturn(limit, batchSize) // add read preference if needed - rp, err := op.createReadPref(desc.Server.Kind, desc.Kind, true) + rp, err := op.createReadPref(desc, true) if err != nil { return dst, info, "", err } @@ -451,7 +451,7 @@ func (op Operation) createLegacyListCollectionsWiremessage(dst []byte, desc desc if err != nil { return dst, info, "", err } - rp, err := op.createReadPref(desc.Server.Kind, desc.Kind, true) + rp, err := op.createReadPref(desc, true) if err != nil { return dst, info, "", err } @@ -466,7 +466,7 @@ func (op Operation) createLegacyListCollectionsWiremessage(dst []byte, desc desc var wmIdx int32 wmIdx, dst = wiremessage.AppendHeaderStart(dst, info.requestID, 0, wiremessage.OpQuery) - dst = wiremessage.AppendQueryFlags(dst, op.slaveOK(desc)) + dst = wiremessage.AppendQueryFlags(dst, op.secondaryOK(desc)) dst = wiremessage.AppendQueryFullCollectionName(dst, op.getFullCollectionName(listCollectionsNamespace)) dst = wiremessage.AppendQueryNumberToSkip(dst, 0) dst = wiremessage.AppendQueryNumberToReturn(dst, batchSize) @@ -607,7 +607,7 @@ func (op Operation) createLegacyListIndexesWiremessage(dst []byte, desc descript filter = bsoncore.AppendStringElement(filter, "ns", op.getFullCollectionName(filterCollName)) filter, _ = bsoncore.AppendDocumentEnd(filter, fidx) - rp, err := op.createReadPref(desc.Server.Kind, desc.Kind, true) + rp, err := op.createReadPref(desc, true) if err != nil { return dst, info, "", err } @@ -617,7 +617,7 @@ func (op Operation) createLegacyListIndexesWiremessage(dst []byte, desc descript var wmIdx int32 wmIdx, dst = wiremessage.AppendHeaderStart(dst, info.requestID, 0, wiremessage.OpQuery) - dst = wiremessage.AppendQueryFlags(dst, op.slaveOK(desc)) + dst = wiremessage.AppendQueryFlags(dst, op.secondaryOK(desc)) dst = wiremessage.AppendQueryFullCollectionName(dst, op.getFullCollectionName(listIndexesNamespace)) dst = wiremessage.AppendQueryNumberToSkip(dst, 0) dst = wiremessage.AppendQueryNumberToReturn(dst, batchSize) diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/session/client_session.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/session/client_session.go index 64adeb71..9e01f9fe 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/session/client_session.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/session/client_session.go @@ -40,7 +40,7 @@ var ErrAbortTwice = errors.New("cannot call abortTransaction twice") // ErrCommitAfterAbort is returned if commit is called after an abort. var ErrCommitAfterAbort = errors.New("cannot call commitTransaction after calling abortTransaction") -// ErrUnackWCUnsupported is returned if an unacknowledged write concern is supported for a transaciton. +// ErrUnackWCUnsupported is returned if an unacknowledged write concern is supported for a transaction. var ErrUnackWCUnsupported = errors.New("transactions do not support unacknowledged write concerns") // ErrSnapshotTransaction is returned if an transaction is started on a snapshot session. @@ -95,6 +95,7 @@ type LoadBalancedTransactionConnection interface { Description() description.Server Close() error ID() string + ServerConnectionID() *int32 Address() address.Address Stale() bool @@ -345,8 +346,6 @@ func (c *Client) EndSession() { c.Terminated = true c.pool.ReturnSession(c.Server) - - return } // TransactionInProgress returns true if the client session is in an active transaction. @@ -365,7 +364,7 @@ func (c *Client) TransactionRunning() bool { return c != nil && (c.TransactionState == Starting || c.TransactionState == InProgress) } -// TransactionCommitted returns true of the client session just committed a transaciton. +// TransactionCommitted returns true of the client session just committed a transaction. func (c *Client) TransactionCommitted() bool { return c.TransactionState == Committed } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/session/server_session.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/session/server_session.go index e7881fa1..54152ea0 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/session/server_session.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/session/server_session.go @@ -9,15 +9,11 @@ package session import ( "time" - "crypto/rand" - "go.mongodb.org/mongo-driver/mongo/description" "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" "go.mongodb.org/mongo-driver/x/mongo/driver/uuid" ) -var rander = rand.Reader - // Server is an open session with the server. type Server struct { SessionID bsoncore.Document diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/connection.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/connection.go index bd08b9a5..c91a1b5e 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/connection.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/connection.go @@ -18,7 +18,6 @@ import ( "sync/atomic" "time" - "go.mongodb.org/mongo-driver/event" "go.mongodb.org/mongo-driver/internal" "go.mongodb.org/mongo-driver/mongo/address" "go.mongodb.org/mongo-driver/mongo/description" @@ -28,21 +27,28 @@ import ( "go.mongodb.org/mongo-driver/x/mongo/driver/wiremessage" ) +// Connection state constants. +const ( + connDisconnected int64 = iota + connConnected + connInitialized +) + var globalConnectionID uint64 = 1 var ( defaultMaxMessageSize uint32 = 48000000 - errResponseTooLarge error = errors.New("length of read message too large") + errResponseTooLarge = errors.New("length of read message too large") errLoadBalancedStateMismatch = errors.New("driver attempted to initialize in load balancing mode, but the server does not support this mode") ) func nextConnectionID() uint64 { return atomic.AddUint64(&globalConnectionID, 1) } type connection struct { - // connected must be accessed using the atomic package and should be at the beginning of the struct. + // state must be accessed using the atomic package and should be at the beginning of the struct. // - atomic bug: https://pkg.go.dev/sync/atomic#pkg-note-BUG // - suggested layout: https://go101.org/article/memory-layout.html - connected int64 + state int64 id string nc net.Conn // When nil, the connection is closed. @@ -51,14 +57,12 @@ type connection struct { idleDeadline atomic.Value // Stores a time.Time readTimeout time.Duration writeTimeout time.Duration - descMu sync.RWMutex // Guards desc. TODO: Remove with or after GODRIVER-2038. desc description.Server - isMasterRTT time.Duration + helloRTT time.Duration compressor wiremessage.CompressorID zliblevel int zstdLevel int connectDone chan struct{} - connectErr error config *connectionConfig cancelConnectContext context.CancelFunc connectContextMade chan struct{} @@ -66,21 +70,17 @@ type connection struct { currentlyStreaming bool connectContextMutex sync.Mutex cancellationListener cancellationListener + serverConnectionID *int32 // the server's ID for this client's connection // pool related fields - pool *pool - poolID uint64 - generation uint64 - expireReason string - poolMonitor *event.PoolMonitor + pool *pool + poolID uint64 + generation uint64 } // newConnection handles the creation of a connection. It does not connect the connection. -func newConnection(addr address.Address, opts ...ConnectionOption) (*connection, error) { - cfg, err := newConnectionConfig(opts...) - if err != nil { - return nil, err - } +func newConnection(addr address.Address, opts ...ConnectionOption) *connection { + cfg := newConnectionConfig(opts...) id := fmt.Sprintf("%s[-%d]", addr, nextConnectionID()) @@ -94,28 +94,15 @@ func newConnection(addr address.Address, opts ...ConnectionOption) (*connection, config: cfg, connectContextMade: make(chan struct{}), cancellationListener: internal.NewCancellationListener(), - poolMonitor: cfg.poolMonitor, } // Connections to non-load balanced deployments should eagerly set the generation numbers so errors encountered // at any point during connection establishment can be processed without the connection being considered stale. if !c.config.loadBalanced { c.setGenerationNumber() } - atomic.StoreInt64(&c.connected, initialized) + atomic.StoreInt64(&c.state, connInitialized) - return c, nil -} - -func (c *connection) processInitializationError(opCtx context.Context, err error) { - atomic.StoreInt64(&c.connected, disconnected) - if c.nc != nil { - _ = c.nc.Close() - } - - c.connectErr = ConnectionError{Wrapped: err, init: true} - if c.config.errorHandlingCallback != nil { - c.config.errorHandlingCallback(opCtx, c.connectErr, c.generation, c.desc.ServiceID) - } + return c } // setGenerationNumber sets the connection's generation number if a callback has been provided to do so in connection @@ -139,14 +126,28 @@ func (c *connection) hasGenerationNumber() bool { return c.desc.LoadBalanced() } -// connect handles the I/O for a connection. It will dial, configure TLS, and perform -// initialization handshakes. -func (c *connection) connect(ctx context.Context) { - if !atomic.CompareAndSwapInt64(&c.connected, initialized, connected) { - return +// connect handles the I/O for a connection. It will dial, configure TLS, and perform initialization +// handshakes. All errors returned by connect are considered "before the handshake completes" and +// must be handled by calling the appropriate SDAM handshake error handler. +func (c *connection) connect(ctx context.Context) (err error) { + if !atomic.CompareAndSwapInt64(&c.state, connInitialized, connConnected) { + return nil } + defer close(c.connectDone) + // If connect returns an error, set the connection status as disconnected and close the + // underlying net.Conn if it was created. + defer func() { + if err != nil { + atomic.StoreInt64(&c.state, connDisconnected) + + if c.nc != nil { + _ = c.nc.Close() + } + } + }() + // Create separate contexts for dialing a connection and doing the MongoDB/auth handshakes. // // handshakeCtx is simply a cancellable version of ctx because there's no default timeout that needs to be applied @@ -185,12 +186,9 @@ func (c *connection) connect(ctx context.Context) { close(c.connectContextMade) // Assign the result of DialContext to a temporary net.Conn to ensure that c.nc is not set in an error case. - var err error - var tempNc net.Conn - tempNc, err = c.config.dialer.DialContext(dialCtx, c.addr.Network(), c.addr.String()) + tempNc, err := c.config.dialer.DialContext(dialCtx, c.addr.Network(), c.addr.String()) if err != nil { - c.processInitializationError(ctx, err) - return + return ConnectionError{Wrapped: err, init: true} } c.nc = tempNc @@ -205,25 +203,15 @@ func (c *connection) connect(ctx context.Context) { } tlsNc, err := configureTLS(dialCtx, c.config.tlsConnectionSource, c.nc, c.addr, tlsConfig, ocspOpts) if err != nil { - c.processInitializationError(ctx, err) - return + return ConnectionError{Wrapped: err, init: true} } c.nc = tlsNc } - c.bumpIdleDeadline() - - // running isMaster and authentication is handled by a handshaker on the configuration instance. + // running hello and authentication is handled by a handshaker on the configuration instance. handshaker := c.config.handshaker if handshaker == nil { - if c.poolMonitor != nil { - c.poolMonitor.Event(&event.PoolEvent{ - Type: event.ConnectionReady, - Address: c.addr.String(), - ConnectionID: c.poolID, - }) - } - return + return nil } var handshakeInfo driver.HandshakeInformation @@ -233,10 +221,9 @@ func (c *connection) connect(ctx context.Context) { if err == nil { // We only need to retain the Description field as the connection's description. The authentication-related // fields in handshakeInfo are tracked by the handshaker if necessary. - c.descMu.Lock() c.desc = handshakeInfo.Description - c.descMu.Unlock() - c.isMasterRTT = time.Since(handshakeStartTime) + c.serverConnectionID = handshakeInfo.ServerConnectionID + c.helloRTT = time.Since(handshakeStartTime) // If the application has indicated that the cluster is load balanced, ensure the server has included serviceId // in its handshake response to signal that it knows it's behind an LB as well. @@ -259,8 +246,7 @@ func (c *connection) connect(ctx context.Context) { // We have a failed handshake here if err != nil { - c.processInitializationError(ctx, err) - return + return ConnectionError{Wrapped: err, init: true} } if len(c.desc.Compression) > 0 { @@ -291,20 +277,13 @@ func (c *connection) connect(ctx context.Context) { } } } - if c.poolMonitor != nil { - c.poolMonitor.Event(&event.PoolEvent{ - Type: event.ConnectionReady, - Address: c.addr.String(), - ConnectionID: c.poolID, - }) - } + return nil } -func (c *connection) wait() error { +func (c *connection) wait() { if c.connectDone != nil { <-c.connectDone } - return c.connectErr } func (c *connection) closeConnectContext() { @@ -349,7 +328,7 @@ func (c *connection) cancellationListenerCallback() { func (c *connection) writeWireMessage(ctx context.Context, wm []byte) error { var err error - if atomic.LoadInt64(&c.connected) != connected { + if atomic.LoadInt64(&c.state) != connConnected { return ConnectionError{ConnectionID: c.id, message: "connection is closed"} } select { @@ -383,7 +362,6 @@ func (c *connection) writeWireMessage(ctx context.Context, wm []byte) error { } } - c.bumpIdleDeadline() return nil } @@ -406,7 +384,7 @@ func (c *connection) write(ctx context.Context, wm []byte) (err error) { // readWireMessage reads a wiremessage from the connection. The dst parameter will be overwritten. func (c *connection) readWireMessage(ctx context.Context, dst []byte) ([]byte, error) { - if atomic.LoadInt64(&c.connected) != connected { + if atomic.LoadInt64(&c.state) != connConnected { return dst, ConnectionError{ConnectionID: c.id, message: "connection is closed"} } @@ -448,7 +426,6 @@ func (c *connection) readWireMessage(ctx context.Context, dst []byte) ([]byte, e } } - c.bumpIdleDeadline() return dst, nil } @@ -480,7 +457,7 @@ func (c *connection) read(ctx context.Context, dst []byte) (bytesRead []byte, er // read the length as an int32 size := (int32(sizeBuf[0])) | (int32(sizeBuf[1]) << 8) | (int32(sizeBuf[2]) << 16) | (int32(sizeBuf[3]) << 24) - // In the case of an isMaster response where MaxMessageSize has not yet been set, use the hard-coded + // In the case of a hello response where MaxMessageSize has not yet been set, use the hard-coded // defaultMaxMessageSize instead. maxMessageSize := c.desc.MaxMessageSize if maxMessageSize == 0 { @@ -509,7 +486,7 @@ func (c *connection) read(ctx context.Context, dst []byte) (bytesRead []byte, er func (c *connection) close() error { // Overwrite the connection state as the first step so only the first close call will execute. - if !atomic.CompareAndSwapInt64(&c.connected, connected, disconnected) { + if !atomic.CompareAndSwapInt64(&c.state, connConnected, connDisconnected) { return nil } @@ -522,7 +499,7 @@ func (c *connection) close() error { } func (c *connection) closed() bool { - return atomic.LoadInt64(&c.connected) == disconnected + return atomic.LoadInt64(&c.state) == connDisconnected } func (c *connection) idleTimeoutExpired() bool { @@ -568,6 +545,10 @@ func (c *connection) ID() string { return c.id } +func (c *connection) ServerConnectionID() *int32 { + return c.serverConnectionID +} + // initConnection is an adapter used during connection initialization. It has the minimum // functionality necessary to implement the driver.Connection interface, which is required to pass a // *connection to a Handshaker. @@ -615,6 +596,10 @@ type Connection struct { refCount int cleanupPoolFn func() + // cleanupServerFn resets the server state when a connection is returned to the connection pool + // via Close() or expired via Expire(). + cleanupServerFn func() + mu sync.RWMutex } @@ -711,11 +696,15 @@ func (c *Connection) Expire() error { } func (c *Connection) cleanupReferences() error { - err := c.pool.put(c.connection) + err := c.pool.checkIn(c.connection) if c.cleanupPoolFn != nil { c.cleanupPoolFn() c.cleanupPoolFn = nil } + if c.cleanupServerFn != nil { + c.cleanupServerFn() + c.cleanupServerFn = nil + } c.connection = nil return err } @@ -814,9 +803,6 @@ func (c *Connection) unpin(reason string) error { return nil } -var notMasterCodes = []int32{10107, 13435} -var recoveringCodes = []int32{11600, 11602, 13436, 189, 91} - func configureTLS(ctx context.Context, tlsConnSource tlsConnectionSource, nc net.Conn, @@ -824,7 +810,6 @@ func configureTLS(ctx context.Context, config *tls.Config, ocspOpts *ocsp.VerifyOptions, ) (net.Conn, error) { - // Ensure config.ServerName is always set for SNI. if config.ServerName == "" { hostname := addr.String() @@ -838,27 +823,15 @@ func configureTLS(ctx context.Context, } client := tlsConnSource.Client(nc, config) - errChan := make(chan error, 1) - go func() { - errChan <- client.Handshake() - }() - - select { - case err := <-errChan: - if err != nil { - return nil, err - } - - // Only do OCSP verification if TLS verification is requested. - if config.InsecureSkipVerify { - break - } + if err := clientHandshake(ctx, client); err != nil { + return nil, err + } + // Only do OCSP verification if TLS verification is requested. + if !config.InsecureSkipVerify { if ocspErr := ocsp.Verify(ctx, client.ConnectionState(), ocspOpts); ocspErr != nil { return nil, ocspErr } - case <-ctx.Done(): - return nil, ctx.Err() } return client, nil } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/connection_legacy_command_metadata.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/connection_legacy_command_metadata.go deleted file mode 100644 index dc333efb..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/connection_legacy_command_metadata.go +++ /dev/null @@ -1,28 +0,0 @@ -package topology - -import "time" - -// commandMetadata contains metadata about a command sent to the server. -type commandMetadata struct { - Name string - Time time.Time - Legacy bool - FullCollectionName string -} - -// createMetadata creates metadata for a command. -func createMetadata(name string, legacy bool, fullCollName string) *commandMetadata { - return &commandMetadata{ - Name: name, - Time: time.Now(), - Legacy: legacy, - FullCollectionName: fullCollName, - } -} - -// TimeDifference returns the difference between now and the time a command was sent in nanoseconds. -func (cm *commandMetadata) TimeDifference() int64 { - t := time.Now() - duration := t.Sub(cm.Time) - return duration.Nanoseconds() -} diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/connection_options.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/connection_options.go index b9dac8c4..24507aa1 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/connection_options.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/connection_options.go @@ -40,13 +40,11 @@ type Handshaker = driver.Handshaker type generationNumberFn func(serviceID *primitive.ObjectID) uint64 type connectionConfig struct { - appName string connectTimeout time.Duration dialer Dialer handshaker Handshaker idleTimeout time.Duration cmdMonitor *event.CommandMonitor - poolMonitor *event.PoolMonitor readTimeout time.Duration writeTimeout time.Duration tlsConfig *tls.Config @@ -55,13 +53,12 @@ type connectionConfig struct { zstdLevel *int ocspCache ocsp.Cache disableOCSPEndpointCheck bool - errorHandlingCallback func(opCtx context.Context, err error, startGenNum uint64, svcID *primitive.ObjectID) tlsConnectionSource tlsConnectionSource loadBalanced bool getGenerationFn generationNumberFn } -func newConnectionConfig(opts ...ConnectionOption) (*connectionConfig, error) { +func newConnectionConfig(opts ...ConnectionOption) *connectionConfig { cfg := &connectionConfig{ connectTimeout: 30 * time.Second, dialer: nil, @@ -69,139 +66,111 @@ func newConnectionConfig(opts ...ConnectionOption) (*connectionConfig, error) { } for _, opt := range opts { - err := opt(cfg) - if err != nil { - return nil, err + if opt == nil { + continue } + opt(cfg) } if cfg.dialer == nil { cfg.dialer = &net.Dialer{} } - return cfg, nil + return cfg } // ConnectionOption is used to configure a connection. -type ConnectionOption func(*connectionConfig) error +type ConnectionOption func(*connectionConfig) func withTLSConnectionSource(fn func(tlsConnectionSource) tlsConnectionSource) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.tlsConnectionSource = fn(c.tlsConnectionSource) - return nil - } -} - -func withErrorHandlingCallback(fn func(opCtx context.Context, err error, startGenNum uint64, svcID *primitive.ObjectID)) ConnectionOption { - return func(c *connectionConfig) error { - c.errorHandlingCallback = fn - return nil } } // WithCompressors sets the compressors that can be used for communication. func WithCompressors(fn func([]string) []string) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.compressors = fn(c.compressors) - return nil } } // WithConnectTimeout configures the maximum amount of time a dial will wait for a // Connect to complete. The default is 30 seconds. func WithConnectTimeout(fn func(time.Duration) time.Duration) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.connectTimeout = fn(c.connectTimeout) - return nil } } // WithDialer configures the Dialer to use when making a new connection to MongoDB. func WithDialer(fn func(Dialer) Dialer) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.dialer = fn(c.dialer) - return nil } } // WithHandshaker configures the Handshaker that wll be used to initialize newly // dialed connections. func WithHandshaker(fn func(Handshaker) Handshaker) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.handshaker = fn(c.handshaker) - return nil } } // WithIdleTimeout configures the maximum idle time to allow for a connection. func WithIdleTimeout(fn func(time.Duration) time.Duration) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.idleTimeout = fn(c.idleTimeout) - return nil } } // WithReadTimeout configures the maximum read time for a connection. func WithReadTimeout(fn func(time.Duration) time.Duration) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.readTimeout = fn(c.readTimeout) - return nil } } // WithWriteTimeout configures the maximum write time for a connection. func WithWriteTimeout(fn func(time.Duration) time.Duration) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.writeTimeout = fn(c.writeTimeout) - return nil } } // WithTLSConfig configures the TLS options for a connection. func WithTLSConfig(fn func(*tls.Config) *tls.Config) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.tlsConfig = fn(c.tlsConfig) - return nil } } // WithMonitor configures a event for command monitoring. func WithMonitor(fn func(*event.CommandMonitor) *event.CommandMonitor) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.cmdMonitor = fn(c.cmdMonitor) - return nil - } -} - -// withPoolMonitor configures a event for connection monitoring. -func withPoolMonitor(fn func(*event.PoolMonitor) *event.PoolMonitor) ConnectionOption { - return func(c *connectionConfig) error { - c.poolMonitor = fn(c.poolMonitor) - return nil } } // WithZlibLevel sets the zLib compression level. func WithZlibLevel(fn func(*int) *int) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.zlibLevel = fn(c.zlibLevel) - return nil } } // WithZstdLevel sets the zstd compression level. func WithZstdLevel(fn func(*int) *int) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.zstdLevel = fn(c.zstdLevel) - return nil } } // WithOCSPCache specifies a cache to use for OCSP verification. func WithOCSPCache(fn func(ocsp.Cache) ocsp.Cache) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.ocspCache = fn(c.ocspCache) - return nil } } @@ -209,23 +178,20 @@ func WithOCSPCache(fn func(ocsp.Cache) ocsp.Cache) ConnectionOption { // to true, the driver will only check stapled responses and will continue the connection without reaching out to // OCSP responders. func WithDisableOCSPEndpointCheck(fn func(bool) bool) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.disableOCSPEndpointCheck = fn(c.disableOCSPEndpointCheck) - return nil } } // WithConnectionLoadBalanced specifies whether or not the connection is to a server behind a load balancer. func WithConnectionLoadBalanced(fn func(bool) bool) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.loadBalanced = fn(c.loadBalanced) - return nil } } func withGenerationNumberFn(fn func(generationNumberFn) generationNumberFn) ConnectionOption { - return func(c *connectionConfig) error { + return func(c *connectionConfig) { c.getGenerationFn = fn(c.getGenerationFn) - return nil } } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/errors.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/errors.go index f3e2e3a7..d3192cbb 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/errors.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/errors.go @@ -11,7 +11,7 @@ type ConnectionError struct { ConnectionID string Wrapped error - // init will be set to true if this error occured during connection initialization or + // init will be set to true if this error occurred during connection initialization or // during a connection handshake. init bool message string @@ -21,7 +21,7 @@ type ConnectionError struct { func (e ConnectionError) Error() string { message := e.message if e.init { - fullMsg := "error occured during connection handshake" + fullMsg := "error occurred during connection handshake" if message != "" { fullMsg = fmt.Sprintf("%s: %s", fullMsg, message) } @@ -66,6 +66,7 @@ type WaitQueueTimeoutError struct { PinnedCursorConnections uint64 PinnedTransactionConnections uint64 maxPoolSize uint64 + totalConnectionCount int } // Error implements the error interface. @@ -75,10 +76,14 @@ func (w WaitQueueTimeoutError) Error() string { errorMsg = fmt.Sprintf("%s: %s", errorMsg, w.Wrapped.Error()) } - errorMsg = fmt.Sprintf("%s; maxPoolSize: %d, connections in use by cursors: %d, connections in use by transactions: %d", - errorMsg, w.maxPoolSize, w.PinnedCursorConnections, w.PinnedTransactionConnections) - return fmt.Sprintf("%s, connections in use by other operations: %d", errorMsg, - w.maxPoolSize-(w.PinnedCursorConnections+w.PinnedTransactionConnections)) + return fmt.Sprintf( + "%s; maxPoolSize: %d, connections in use by cursors: %d"+ + ", connections in use by transactions: %d, connections in use by other operations: %d", + errorMsg, + w.maxPoolSize, + w.PinnedCursorConnections, + w.PinnedTransactionConnections, + uint64(w.totalConnectionCount)-w.PinnedCursorConnections-w.PinnedTransactionConnections) } // Unwrap returns the underlying error. diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/fsm.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/fsm.go index 7a7f7b3c..46bcd19a 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/fsm.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/fsm.go @@ -18,7 +18,7 @@ import ( var ( // SupportedWireVersions is the range of wire versions supported by the driver. - SupportedWireVersions = description.NewVersionRange(2, 13) + SupportedWireVersions = description.NewVersionRange(2, 15) ) const ( @@ -46,7 +46,7 @@ func newFSM() *fsm { // // apply should operation on immutable descriptions so we don't have to lock for the entire time we're applying the // server description. -func (f *fsm) apply(s description.Server) (description.Topology, description.Server, error) { +func (f *fsm) apply(s description.Server) (description.Topology, description.Server) { newServers := make([]description.Server, len(f.Servers)) copy(newServers, f.Servers) @@ -77,7 +77,7 @@ func (f *fsm) apply(s description.Server) (description.Topology, description.Ser } if _, ok := f.findServer(s.Addr); !ok { - return f.Topology, s, nil + return f.Topology, s } updatedDesc := s @@ -107,7 +107,7 @@ func (f *fsm) apply(s description.Server) (description.Topology, description.Ser MinSupportedMongoDBVersion, ) f.Topology.CompatibilityErr = f.compatibilityErr - return f.Topology, s, nil + return f.Topology, s } if server.WireVersion.Min > SupportedWireVersions.Max { @@ -119,14 +119,14 @@ func (f *fsm) apply(s description.Server) (description.Topology, description.Ser SupportedWireVersions.Max, ) f.Topology.CompatibilityErr = f.compatibilityErr - return f.Topology, s, nil + return f.Topology, s } } } f.compatible.Store(true) f.compatibilityErr = nil - return f.Topology, updatedDesc, nil + return f.Topology, updatedDesc } func (f *fsm) applyToReplicaSetNoPrimary(s description.Server) description.Server { @@ -185,7 +185,7 @@ func (f *fsm) applyToSingle(s description.Server) description.Server { f.replaceServer(s) case description.RSPrimary, description.RSSecondary, description.RSArbiter, description.RSMember, description.RSGhost: // A replica set name can be provided when creating a direct connection. In this case, if the set name returned - // by the isMaster response doesn't match up with the one provided during configuration, the server description + // by the hello response doesn't match up with the one provided during configuration, the server description // is replaced with a default Unknown description. // // We create a new server description rather than doing s.Kind = description.Unknown because the other fields, @@ -239,7 +239,7 @@ func (f *fsm) updateRSFromPrimary(s description.Server) { return } - if s.SetVersion != 0 && !bytes.Equal(s.ElectionID[:], primitive.NilObjectID[:]) { + if s.SetVersion != 0 && !s.ElectionID.IsZero() { if f.maxSetVersion > s.SetVersion || bytes.Compare(f.maxElectionID[:], s.ElectionID[:]) == 1 { f.replaceServer(description.Server{ Addr: s.Addr, @@ -376,12 +376,10 @@ func (f *fsm) removeServerByAddr(addr address.Address) { } } -func (f *fsm) replaceServer(s description.Server) bool { +func (f *fsm) replaceServer(s description.Server) { if i, ok := f.findServer(s.Addr); ok { f.setServer(i, s) - return true } - return false } func (f *fsm) setServer(i int, s description.Server) { diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/hanging_tls_conn_1_16.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/hanging_tls_conn_1_16.go new file mode 100644 index 00000000..b4a690c4 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/hanging_tls_conn_1_16.go @@ -0,0 +1,37 @@ +// Copyright (C) MongoDB, Inc. 2022-present. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + +//go:build !go1.17 +// +build !go1.17 + +package topology + +import ( + "crypto/tls" + "time" +) + +// hangingTLSConn is an implementation of tlsConn that wraps the tls.Conn type and overrides the Handshake function to +// sleep for a fixed amount of time. +type hangingTLSConn struct { + *tls.Conn + sleepTime time.Duration +} + +var _ tlsConn = (*hangingTLSConn)(nil) + +func newHangingTLSConn(conn *tls.Conn, sleepTime time.Duration) *hangingTLSConn { + return &hangingTLSConn{ + Conn: conn, + sleepTime: sleepTime, + } +} + +// Handshake implements the tlsConn interface on Go 1.16 and less. +func (h *hangingTLSConn) Handshake() error { + time.Sleep(h.sleepTime) + return h.Conn.Handshake() +} diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/hanging_tls_conn_1_17.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/hanging_tls_conn_1_17.go new file mode 100644 index 00000000..1058b86c --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/hanging_tls_conn_1_17.go @@ -0,0 +1,44 @@ +// Copyright (C) MongoDB, Inc. 2022-present. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + +//go:build go1.17 +// +build go1.17 + +package topology + +import ( + "context" + "crypto/tls" + "time" +) + +// hangingTLSConn is an implementation of tlsConn that wraps the tls.Conn type and overrides the HandshakeContext function to +// sleep for a fixed amount of time. +type hangingTLSConn struct { + *tls.Conn + sleepTime time.Duration +} + +var _ tlsConn = (*hangingTLSConn)(nil) + +func newHangingTLSConn(conn *tls.Conn, sleepTime time.Duration) *hangingTLSConn { + return &hangingTLSConn{ + Conn: conn, + sleepTime: sleepTime, + } +} + +// HandshakeContext implements the tlsConn interface on Go 1.17 and higher. +func (h *hangingTLSConn) HandshakeContext(ctx context.Context) error { + timer := time.NewTimer(h.sleepTime) + defer timer.Stop() + select { + case <-timer.C: + case <-ctx.Done(): + } + + return h.Conn.HandshakeContext(ctx) +} diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/pool.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/pool.go index ae5d65f2..f63ed796 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/pool.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/pool.go @@ -8,7 +8,7 @@ package topology import ( "context" - "math" + "fmt" "sync" "sync/atomic" "time" @@ -16,15 +16,22 @@ import ( "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/event" "go.mongodb.org/mongo-driver/mongo/address" - "golang.org/x/sync/semaphore" + "go.mongodb.org/mongo-driver/x/mongo/driver" ) -// ErrPoolConnected is returned from an attempt to connect an already connected pool -var ErrPoolConnected = PoolError("attempted to Connect to an already connected pool") +// Connection pool state constants. +const ( + poolPaused int = iota + poolReady + poolClosed +) + +// ErrPoolNotPaused is returned when attempting to mark a connection pool "ready" that is not +// currently "paused". +var ErrPoolNotPaused = PoolError("only a paused pool can be marked ready") -// ErrPoolDisconnected is returned from an attempt to Close an already disconnected -// or disconnecting pool. -var ErrPoolDisconnected = PoolError("attempted to check out a connection from closed connection pool") +// ErrPoolClosed is returned when attempting to check out a connection from a closed pool. +var ErrPoolClosed = PoolError("attempted to check out a connection from closed connection pool") // ErrConnectionClosed is returned from an attempt to use an already closed connection. var ErrConnectionClosed = ConnectionError{ConnectionID: "", message: "connection is closed"} @@ -35,295 +42,312 @@ var ErrWrongPool = PoolError("connection does not belong to this pool") // PoolError is an error returned from a Pool method. type PoolError string -// maintainInterval is the interval at which the background routine to close stale connections will be run. -var maintainInterval = time.Minute - func (pe PoolError) Error() string { return string(pe) } -// poolConfig contains all aspects of the pool that can be configured -type poolConfig struct { - Address address.Address - MinPoolSize uint64 - MaxPoolSize uint64 // MaxPoolSize is not used because handling the max number of connections in the pool is handled in server. This is only used for command monitoring - MaxIdleTime time.Duration - PoolMonitor *event.PoolMonitor +// poolClearedError is an error returned when the connection pool is cleared or currently paused. It +// is a retryable error. +type poolClearedError struct { + err error + address address.Address +} + +func (pce poolClearedError) Error() string { + return fmt.Sprintf( + "connection pool for %v was cleared because another operation failed with: %v", + pce.address, + pce.err) } -// checkOutResult is all the values that can be returned from a checkOut -type checkOutResult struct { - c *connection - err error - reason string +// Retryable returns true. All poolClearedErrors are retryable. +func (poolClearedError) Retryable() bool { return true } + +// Assert that poolClearedError is a driver.RetryablePoolError. +var _ driver.RetryablePoolError = poolClearedError{} + +// poolConfig contains all aspects of the pool that can be configured +type poolConfig struct { + Address address.Address + MinPoolSize uint64 + MaxPoolSize uint64 + MaxConnecting uint64 + MaxIdleTime time.Duration + MaintainInterval time.Duration + PoolMonitor *event.PoolMonitor + handshakeErrFn func(error, uint64, *primitive.ObjectID) } -// pool is a wrapper of resource pool that follows the CMAP spec for connection pools type pool struct { - // These fields must be accessed using the atomic package and should be at the beginning of the struct. + // The following integer fields must be accessed using the atomic package + // and should be at the beginning of the struct. // - atomic bug: https://pkg.go.dev/sync/atomic#pkg-note-BUG // - suggested layout: https://go101.org/article/memory-layout.html - connected int64 + + nextID uint64 // nextID is the next pool ID for a new connection. pinnedCursorConnections uint64 pinnedTransactionConnections uint64 - nextid uint64 - opened map[uint64]*connection // opened holds all of the currently open connections. - sem *semaphore.Weighted - sync.Mutex + address address.Address + minSize uint64 + maxSize uint64 + maxConnecting uint64 + monitor *event.PoolMonitor - address address.Address - opts []ConnectionOption - conns *resourcePool // pool for non-checked out connections + // handshakeErrFn is used to handle any errors that happen during connection establishment and + // handshaking. + handshakeErrFn func(error, uint64, *primitive.ObjectID) + + connOpts []ConnectionOption generation *poolGenerationMap - monitor *event.PoolMonitor + + maintainInterval time.Duration // maintainInterval is the maintain() loop interval. + maintainReady chan struct{} // maintainReady is a signal channel that starts the maintain() loop when ready() is called. + backgroundDone *sync.WaitGroup // backgroundDone waits for all background goroutines to return. + + stateMu sync.RWMutex // stateMu guards state, lastClearErr + state int // state is the current state of the connection pool. + lastClearErr error // lastClearErr is the last error that caused the pool to be cleared. + + // createConnectionsCond is the condition variable that controls when the createConnections() + // loop runs or waits. Its lock guards cancelBackgroundCtx, conns, and newConnWait. Any changes + // to the state of the guarded values must be made while holding the lock to prevent undefined + // behavior in the createConnections() waiting logic. + createConnectionsCond *sync.Cond + cancelBackgroundCtx context.CancelFunc // cancelBackgroundCtx is called to signal background goroutines to stop. + conns map[uint64]*connection // conns holds all currently open connections. + newConnWait wantConnQueue // newConnWait holds all wantConn requests for new connections. + + idleMu sync.Mutex // idleMu guards idleConns, idleConnWait + idleConns []*connection // idleConns holds all idle connections. + idleConnWait wantConnQueue // idleConnWait holds all wantConn requests for idle connections. } -// connectionExpiredFunc checks if a given connection is stale and should be removed from the resource pool -func connectionExpiredFunc(v interface{}) bool { - if v == nil { - return true - } +// getState returns the current state of the pool. Callers must not hold the stateMu lock. +func (p *pool) getState() int { + p.stateMu.RLock() + defer p.stateMu.RUnlock() - c, ok := v.(*connection) - if !ok { - return true - } + return p.state +} +// connectionPerished checks if a given connection is perished and should be removed from the pool. +func connectionPerished(conn *connection) (string, bool) { switch { - case atomic.LoadInt64(&c.pool.connected) != connected: - c.expireReason = event.ReasonPoolClosed - case c.closed(): + case conn.closed(): // A connection would only be closed if it encountered a network error during an operation and closed itself. - c.expireReason = event.ReasonConnectionErrored - case c.idleTimeoutExpired(): - c.expireReason = event.ReasonIdle - case c.pool.stale(c): - c.expireReason = event.ReasonStale - default: - return false + return event.ReasonError, true + case conn.idleTimeoutExpired(): + return event.ReasonIdle, true + case conn.pool.stale(conn): + return event.ReasonStale, true } - - return true + return "", false } -// connectionCloseFunc closes a given connection. If ctx is nil, the closing will occur in the background -func connectionCloseFunc(v interface{}) { - c, ok := v.(*connection) - if !ok || v == nil { - return +// newPool creates a new pool. It will use the provided options when creating connections. +func newPool(config poolConfig, connOpts ...ConnectionOption) *pool { + if config.MaxIdleTime != time.Duration(0) { + connOpts = append(connOpts, WithIdleTimeout(func(_ time.Duration) time.Duration { return config.MaxIdleTime })) } - // The resource pool will only close connections if they're expired or the pool is being disconnected and - // resourcePool.Close() is called. For the former case, c.expireReason will be set. In the latter, it will not, so - // we use ReasonPoolClosed. - reason := c.expireReason - if c.expireReason == "" { - reason = event.ReasonPoolClosed + var maxConnecting uint64 = 2 + if config.MaxConnecting > 0 { + maxConnecting = config.MaxConnecting } - _ = c.pool.removeConnection(c, reason) - go func() { - _ = c.pool.closeConnection(c) - }() -} + maintainInterval := 10 * time.Second + if config.MaintainInterval != 0 { + maintainInterval = config.MaintainInterval + } -// connectionInitFunc returns an init function for the resource pool that will make new connections for this pool -func (p *pool) connectionInitFunc() interface{} { - c, _, err := p.makeNewConnection() - if err != nil { - return nil + pool := &pool{ + address: config.Address, + minSize: config.MinPoolSize, + maxSize: config.MaxPoolSize, + maxConnecting: maxConnecting, + monitor: config.PoolMonitor, + handshakeErrFn: config.handshakeErrFn, + connOpts: connOpts, + generation: newPoolGenerationMap(), + state: poolPaused, + maintainInterval: maintainInterval, + maintainReady: make(chan struct{}, 1), + backgroundDone: &sync.WaitGroup{}, + createConnectionsCond: sync.NewCond(&sync.Mutex{}), + conns: make(map[uint64]*connection, config.MaxPoolSize), + idleConns: make([]*connection, 0, config.MaxPoolSize), } + pool.connOpts = append(pool.connOpts, withGenerationNumberFn(func(_ generationNumberFn) generationNumberFn { return pool.getGenerationForNewConnection })) - go c.connect(context.Background()) + pool.generation.connect() - return c -} + // Create a Context with cancellation that's used to signal the createConnections() and + // maintain() background goroutines to stop. Also create a "backgroundDone" WaitGroup that is + // used to wait for the background goroutines to return. + var ctx context.Context + ctx, pool.cancelBackgroundCtx = context.WithCancel(context.Background()) -// newPool creates a new pool that will hold size number of idle connections. It will use the -// provided options when creating connections. -func newPool(config poolConfig, connOpts ...ConnectionOption) (*pool, error) { - opts := connOpts - if config.MaxIdleTime != time.Duration(0) { - opts = append(opts, WithIdleTimeout(func(_ time.Duration) time.Duration { return config.MaxIdleTime })) - } - if config.PoolMonitor != nil { - opts = append(opts, withPoolMonitor(func(_ *event.PoolMonitor) *event.PoolMonitor { return config.PoolMonitor })) + for i := 0; i < int(pool.maxConnecting); i++ { + pool.backgroundDone.Add(1) + go pool.createConnections(ctx, pool.backgroundDone) } - var maxConns = config.MaxPoolSize - if maxConns == 0 { - maxConns = math.MaxInt64 - } - - pool := &pool{ - address: config.Address, - monitor: config.PoolMonitor, - connected: disconnected, - opened: make(map[uint64]*connection), - opts: opts, - sem: semaphore.NewWeighted(int64(maxConns)), - generation: newPoolGenerationMap(), - } - pool.opts = append(pool.opts, withGenerationNumberFn(func(_ generationNumberFn) generationNumberFn { return pool.getGenerationForNewConnection })) - - // we do not pass in config.MaxPoolSize because we manage the max size at this level rather than the resource pool level - rpc := resourcePoolConfig{ - MaxSize: maxConns, - MinSize: config.MinPoolSize, - MaintainInterval: maintainInterval, - ExpiredFn: connectionExpiredFunc, - CloseFn: connectionCloseFunc, - InitFn: pool.connectionInitFunc, + // If maintainInterval is not positive, don't start the maintain() goroutine. Expect that + // negative values are only used in testing; this config value is not user-configurable. + if maintainInterval > 0 { + pool.backgroundDone.Add(1) + go pool.maintain(ctx, pool.backgroundDone) } if pool.monitor != nil { pool.monitor.Event(&event.PoolEvent{ Type: event.PoolCreated, PoolOptions: &event.MonitorPoolOptions{ - MaxPoolSize: rpc.MaxSize, - MinPoolSize: rpc.MinSize, - WaitQueueTimeoutMS: uint64(config.MaxIdleTime) / uint64(time.Millisecond), + MaxPoolSize: config.MaxPoolSize, + MinPoolSize: config.MinPoolSize, }, Address: pool.address.String(), }) } - rp, err := newResourcePool(rpc) - if err != nil { - return nil, err - } - pool.conns = rp - - return pool, nil + return pool } // stale checks if a given connection's generation is below the generation of the pool -func (p *pool) stale(c *connection) bool { - if c == nil { - return true +func (p *pool) stale(conn *connection) bool { + return conn == nil || p.generation.stale(conn.desc.ServiceID, conn.generation) +} + +// ready puts the pool into the "ready" state and starts the background connection creation and +// monitoring goroutines. ready must be called before connections can be checked out. An unused, +// connected pool must be closed or it will leak goroutines and will not be garbage collected. +func (p *pool) ready() error { + // While holding the stateMu lock, set the pool to "ready" if it is currently "paused". + p.stateMu.Lock() + if p.state == poolReady { + p.stateMu.Unlock() + return nil } + if p.state != poolPaused { + p.stateMu.Unlock() + return ErrPoolNotPaused + } + p.lastClearErr = nil + p.state = poolReady + p.stateMu.Unlock() - c.descMu.RLock() - serviceID := c.desc.ServiceID - c.descMu.RUnlock() - return p.generation.stale(serviceID, c.generation) -} + // Signal maintain() to wake up immediately when marking the pool "ready". + select { + case p.maintainReady <- struct{}{}: + default: + } -// connect puts the pool into the connected state, allowing it to be used and will allow items to begin being processed from the wait queue -func (p *pool) connect() error { - if !atomic.CompareAndSwapInt64(&p.connected, disconnected, connected) { - return ErrPoolConnected + if p.monitor != nil { + p.monitor.Event(&event.PoolEvent{ + Type: event.PoolReady, + Address: p.address.String(), + }) } - p.generation.connect() - p.conns.initialize() + return nil } -// disconnect disconnects the pool and closes all connections including those both in and out of the pool -func (p *pool) disconnect(ctx context.Context) error { - if !atomic.CompareAndSwapInt64(&p.connected, connected, disconnecting) { - return ErrPoolDisconnected +// close closes the pool, closes all connections associated with the pool, and stops all background +// goroutines. All subsequent checkOut requests will return an error. An unused, ready pool must be +// closed or it will leak goroutines and will not be garbage collected. +func (p *pool) close(ctx context.Context) { + p.stateMu.Lock() + if p.state == poolClosed { + p.stateMu.Unlock() + return } + p.state = poolClosed + p.stateMu.Unlock() + + // Call cancelBackgroundCtx() to exit the maintain() and createConnections() background + // goroutines. Broadcast to the createConnectionsCond to wake up all createConnections() + // goroutines. We must hold the createConnectionsCond lock here because we're changing the + // condition by cancelling the "background goroutine" Context, even tho cancelling the Context + // is also synchronized by a lock. Otherwise, we run into an intermittent bug that prevents the + // createConnections() goroutines from exiting. + p.createConnectionsCond.L.Lock() + p.cancelBackgroundCtx() + p.createConnectionsCond.Broadcast() + p.createConnectionsCond.L.Unlock() + + // Wait for all background goroutines to exit. + p.backgroundDone.Wait() + + p.generation.disconnect() if ctx == nil { ctx = context.Background() } - p.conns.Close() - p.generation.disconnect() - - var err error - if dl, ok := ctx.Deadline(); ok { - // If we have a deadline then we interpret it as a request to gracefully shutdown. We wait - // until either all the connections have landed back in the pool (and have been closed) or - // until the timer is done. - ticker := time.NewTicker(1 * time.Second) + // If we have a deadline then we interpret it as a request to gracefully shutdown. We wait until + // either all the connections have been checked back into the pool (i.e. total open connections + // equals idle connections) or until the Context deadline is reached. + if _, ok := ctx.Deadline(); ok { + ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() - timer := time.NewTimer(time.Now().Sub(dl)) - defer timer.Stop() + + graceful: for { + if p.totalConnectionCount() == p.availableConnectionCount() { + break graceful + } + select { - case <-timer.C: + case <-ticker.C: case <-ctx.Done(): - case <-ticker.C: // Can we replace this with an actual signal channel? We will know when p.inflight hits zero from the close method. - p.Lock() - if len(p.opened) > 0 { - p.Unlock() - continue - } - p.Unlock() + break graceful + default: } - break } } - // We copy the remaining connections into a slice, then iterate it to close them. This allows us - // to use a single function to actually clean up and close connections at the expense of a - // double iteration in the worse case. - p.Lock() - toClose := make([]*connection, 0, len(p.opened)) - for _, pc := range p.opened { - toClose = append(toClose, pc) + // Empty the idle connections stack and try to deliver ErrPoolClosed to any waiting wantConns + // from idleConnWait while holding the idleMu lock. + p.idleMu.Lock() + p.idleConns = p.idleConns[:0] + for { + w := p.idleConnWait.popFront() + if w == nil { + break + } + w.tryDeliver(nil, ErrPoolClosed) } - p.Unlock() - for _, pc := range toClose { - _ = p.removeConnection(pc, event.ReasonPoolClosed) - _ = p.closeConnection(pc) // We don't care about errors while closing the connection. + p.idleMu.Unlock() + + // Collect all conns from the pool and try to deliver ErrPoolClosed to any waiting wantConns + // from newConnWait while holding the createConnectionsCond lock. We can't call removeConnection + // on the connections while holding any locks, so do that after we release the lock. + p.createConnectionsCond.L.Lock() + conns := make([]*connection, 0, len(p.conns)) + for _, conn := range p.conns { + conns = append(conns, conn) } - atomic.StoreInt64(&p.connected, disconnected) - p.conns.clearTotal() - - if p.monitor != nil { - p.monitor.Event(&event.PoolEvent{ - Type: event.PoolClosedEvent, - Address: p.address.String(), - }) + for { + w := p.newConnWait.popFront() + if w == nil { + break + } + w.tryDeliver(nil, ErrPoolClosed) } + p.createConnectionsCond.L.Unlock() - return err -} - -// makeNewConnection creates a new connection instance and emits a ConnectionCreatedEvent. The caller must call -// connection.connect on the returned instance before using it for operations. This function ensures that a -// ConnectionClosed event is published if there is an error after the ConnectionCreated event has been published. The -// caller must not hold the pool lock when calling this function. -func (p *pool) makeNewConnection() (*connection, string, error) { - c, err := newConnection(p.address, p.opts...) - if err != nil { - return nil, event.ReasonConnectionErrored, err + // Now that we're not holding any locks, remove all of the connections we collected from the + // pool. + for _, conn := range conns { + _ = p.removeConnection(conn, event.ReasonPoolClosed) + _ = p.closeConnection(conn) // We don't care about errors while closing the connection. } - c.pool = p - c.poolID = atomic.AddUint64(&p.nextid, 1) - if p.monitor != nil { p.monitor.Event(&event.PoolEvent{ - Type: event.ConnectionCreated, - Address: p.address.String(), - ConnectionID: c.poolID, + Type: event.PoolClosedEvent, + Address: p.address.String(), }) } - - if atomic.LoadInt64(&p.connected) != connected { - // Manually publish a ConnectionClosed event here because the connection reference hasn't been stored and we - // need to ensure each ConnectionCreated event has a corresponding ConnectionClosed event. - if p.monitor != nil { - p.monitor.Event(&event.PoolEvent{ - Type: event.ConnectionClosed, - Address: p.address.String(), - ConnectionID: c.poolID, - Reason: event.ReasonPoolClosed, - }) - } - _ = p.closeConnection(c) // The pool is disconnected or disconnecting, ignore the error from closing the connection. - return nil, event.ReasonPoolClosed, ErrPoolDisconnected - } - - p.Lock() - p.opened[c.poolID] = c - p.Unlock() - - return c, "", nil - } func (p *pool) pinConnectionToCursor() { @@ -344,13 +368,27 @@ func (p *pool) unpinConnectionFromTransaction() { atomic.AddUint64(&p.pinnedTransactionConnections, ^uint64(0)) } -// Checkout returns a connection from the pool -func (p *pool) get(ctx context.Context) (*connection, error) { - if ctx == nil { - ctx = context.Background() +// checkOut checks out a connection from the pool. If an idle connection is not available, the +// checkOut enters a queue waiting for either the next idle or new connection. If the pool is not +// ready, checkOut returns an error. +// Based partially on https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/net/http/transport.go;l=1324 +func (p *pool) checkOut(ctx context.Context) (conn *connection, err error) { + if p.monitor != nil { + p.monitor.Event(&event.PoolEvent{ + Type: event.GetStarted, + Address: p.address.String(), + }) } - if atomic.LoadInt64(&p.connected) != connected { + // Check the pool state while holding a stateMu read lock. If the pool state is not "ready", + // return an error. Do all of this while holding the stateMu read lock to prevent a state change between + // checking the state and entering the wait queue. Not holding the stateMu read lock here may + // allow a checkOut() to enter the wait queue after clear() pauses the pool and clears the wait + // queue, resulting in createConnections() doing work while the pool is "paused". + p.stateMu.RLock() + switch p.state { + case poolClosed: + p.stateMu.RUnlock() if p.monitor != nil { p.monitor.Event(&event.PoolEvent{ Type: event.GetFailed, @@ -358,163 +396,123 @@ func (p *pool) get(ctx context.Context) (*connection, error) { Reason: event.ReasonPoolClosed, }) } - return nil, ErrPoolDisconnected - } - - err := p.sem.Acquire(ctx, 1) - if err != nil { + return nil, ErrPoolClosed + case poolPaused: + err := poolClearedError{err: p.lastClearErr, address: p.address} + p.stateMu.RUnlock() if p.monitor != nil { p.monitor.Event(&event.PoolEvent{ Type: event.GetFailed, Address: p.address.String(), - Reason: event.ReasonTimedOut, + Reason: event.ReasonConnectionErrored, }) } - errWaitQueueTimeout := WaitQueueTimeoutError{ - Wrapped: ctx.Err(), - PinnedCursorConnections: atomic.LoadUint64(&p.pinnedCursorConnections), - PinnedTransactionConnections: atomic.LoadUint64(&p.pinnedTransactionConnections), - maxPoolSize: p.conns.maxSize, - } - return nil, errWaitQueueTimeout + return nil, err } - // This loop is so that we don't end up with more than maxPoolSize connections if p.conns.Maintain runs between - // calling p.conns.Get() and making the new connection - for { - if atomic.LoadInt64(&p.connected) != connected { + if ctx == nil { + ctx = context.Background() + } + + // Create a wantConn, which we will use to request an existing idle or new connection. Always + // cancel the wantConn if checkOut() returned an error to make sure any delivered connections + // are returned to the pool (e.g. if a connection was delivered immediately after the Context + // timed out). + w := newWantConn() + defer func() { + if err != nil { + w.cancel(p, err) + } + }() + + // Get in the queue for an idle connection. If getOrQueueForIdleConn returns true, it was able to + // immediately deliver an idle connection to the wantConn, so we can return the connection or + // error from the wantConn without waiting for "ready". + if delivered := p.getOrQueueForIdleConn(w); delivered { + // If delivered = true, we didn't enter the wait queue and will return either a connection + // or an error, so unlock the stateMu lock here. + p.stateMu.RUnlock() + + if w.err != nil { if p.monitor != nil { p.monitor.Event(&event.PoolEvent{ Type: event.GetFailed, Address: p.address.String(), - Reason: event.ReasonPoolClosed, + Reason: event.ReasonConnectionErrored, }) } - p.sem.Release(1) - return nil, ErrPoolDisconnected + return nil, w.err } - connVal := p.conns.Get() - if c, ok := connVal.(*connection); ok && connVal != nil { - // call connect if not connected - if atomic.LoadInt64(&c.connected) == initialized { - c.connect(ctx) - } - - err := c.wait() - if err != nil { - // Call removeConnection to remove the connection reference and emit a ConnectionClosed event. - _ = p.removeConnection(c, event.ReasonConnectionErrored) - p.conns.decrementTotal() - p.sem.Release(1) - - if p.monitor != nil { - p.monitor.Event(&event.PoolEvent{ - Type: event.GetFailed, - Address: p.address.String(), - Reason: event.ReasonConnectionErrored, - }) - } - return nil, err - } - - if p.monitor != nil { - p.monitor.Event(&event.PoolEvent{ - Type: event.GetSucceeded, - Address: p.address.String(), - ConnectionID: c.poolID, - }) - } - return c, nil + if p.monitor != nil { + p.monitor.Event(&event.PoolEvent{ + Type: event.GetSucceeded, + Address: p.address.String(), + ConnectionID: w.conn.poolID, + }) } + return w.conn, nil + } - select { - case <-ctx.Done(): + // If we didn't get an immediately available idle connection, also get in the queue for a new + // connection while we're waiting for an idle connection. + p.queueForNewConn(w) + p.stateMu.RUnlock() + + // Wait for either the wantConn to be ready or for the Context to time out. + select { + case <-w.ready: + if w.err != nil { if p.monitor != nil { p.monitor.Event(&event.PoolEvent{ Type: event.GetFailed, Address: p.address.String(), - Reason: event.ReasonTimedOut, + Reason: event.ReasonConnectionErrored, }) } - p.sem.Release(1) - return nil, ctx.Err() - default: - // The pool is empty, so we try to make a new connection. If incrementTotal fails, the resource pool has - // more resources than we previously thought, so we try to get a resource again. - made := p.conns.incrementTotal() - if !made { - continue - } - c, reason, err := p.makeNewConnection() - - if err != nil { - if p.monitor != nil { - // We only publish a GetFailed event because makeNewConnection has already published - // ConnectionClosed if needed. - p.monitor.Event(&event.PoolEvent{ - Type: event.GetFailed, - Address: p.address.String(), - Reason: reason, - }) - } - p.conns.decrementTotal() - p.sem.Release(1) - return nil, err - } - - c.connect(ctx) - // wait for conn to be connected - err = c.wait() - if err != nil { - // Call removeConnection to remove the connection reference and fire a ConnectionClosedEvent. - _ = p.removeConnection(c, event.ReasonConnectionErrored) - p.conns.decrementTotal() - p.sem.Release(1) - - if p.monitor != nil { - p.monitor.Event(&event.PoolEvent{ - Type: event.GetFailed, - Address: p.address.String(), - Reason: event.ReasonConnectionErrored, - }) - } - return nil, err - } + return nil, w.err + } - if p.monitor != nil { - p.monitor.Event(&event.PoolEvent{ - Type: event.GetSucceeded, - Address: p.address.String(), - ConnectionID: c.poolID, - }) - } - return c, nil + if p.monitor != nil { + p.monitor.Event(&event.PoolEvent{ + Type: event.GetSucceeded, + Address: p.address.String(), + ConnectionID: w.conn.poolID, + }) + } + return w.conn, nil + case <-ctx.Done(): + if p.monitor != nil { + p.monitor.Event(&event.PoolEvent{ + Type: event.GetFailed, + Address: p.address.String(), + Reason: event.ReasonTimedOut, + }) + } + return nil, WaitQueueTimeoutError{ + Wrapped: ctx.Err(), + PinnedCursorConnections: atomic.LoadUint64(&p.pinnedCursorConnections), + PinnedTransactionConnections: atomic.LoadUint64(&p.pinnedTransactionConnections), + maxPoolSize: p.maxSize, + totalConnectionCount: p.totalConnectionCount(), } } } -// closeConnection closes a connection, not the pool itself. This method will actually closeConnection the connection, -// making it unusable, to instead return the connection to the pool, use put. -func (p *pool) closeConnection(c *connection) error { - if c.pool != p { +// closeConnection closes a connection. +func (p *pool) closeConnection(conn *connection) error { + if conn.pool != p { return ErrWrongPool } - if atomic.LoadInt64(&c.connected) == connected { - c.closeConnectContext() - _ = c.wait() // Make sure that the connection has finished connecting - } - - if !atomic.CompareAndSwapInt64(&c.connected, connected, disconnected) { - return nil // We're closing an already closed connection + if atomic.LoadInt64(&conn.state) == connConnected { + conn.closeConnectContext() + conn.wait() // Make sure that the connection has finished connecting. } - if c.nc != nil { - err := c.nc.Close() - if err != nil { - return ConnectionError{ConnectionID: c.id, Wrapped: err, message: "failed to close net.Conn"} - } + err := conn.close() + if err != nil { + return ConnectionError{ConnectionID: conn.id, Wrapped: err, message: "failed to close net.Conn"} } return nil @@ -524,80 +522,608 @@ func (p *pool) getGenerationForNewConnection(serviceID *primitive.ObjectID) uint return p.generation.addConnection(serviceID) } -// removeConnection removes a connection from the pool. -func (p *pool) removeConnection(c *connection, reason string) error { - if c.pool != p { - return ErrWrongPool +// removeConnection removes a connection from the pool and emits a "ConnectionClosed" event. +func (p *pool) removeConnection(conn *connection, reason string) error { + if conn == nil { + return nil } - var publishEvent bool - p.Lock() - if _, ok := p.opened[c.poolID]; ok { - publishEvent = true - delete(p.opened, c.poolID) + if conn.pool != p { + return ErrWrongPool } - p.Unlock() - // Only update the generation numbers map if the connection has retrieved its generation number. Otherwise, we'd - // decrement the count for the generation even though it had never been incremented. - if c.hasGenerationNumber() { - c.descMu.RLock() - serviceID := c.desc.ServiceID - c.descMu.RUnlock() - p.generation.removeConnection(serviceID) + p.createConnectionsCond.L.Lock() + _, ok := p.conns[conn.poolID] + if !ok { + // If the connection has been removed from the pool already, exit without doing any + // additional state changes. + p.createConnectionsCond.L.Unlock() + return nil + } + delete(p.conns, conn.poolID) + // Signal the createConnectionsCond so any goroutines waiting for a new connection slot in the + // pool will proceed. + p.createConnectionsCond.Signal() + p.createConnectionsCond.L.Unlock() + + // Only update the generation numbers map if the connection has retrieved its generation number. + // Otherwise, we'd decrement the count for the generation even though it had never been + // incremented. + if conn.hasGenerationNumber() { + p.generation.removeConnection(conn.desc.ServiceID) } - if publishEvent && p.monitor != nil { - c.pool.monitor.Event(&event.PoolEvent{ + if p.monitor != nil { + p.monitor.Event(&event.PoolEvent{ Type: event.ConnectionClosed, - Address: c.pool.address.String(), - ConnectionID: c.poolID, + Address: p.address.String(), + ConnectionID: conn.poolID, Reason: reason, }) } + return nil } -// put returns a connection to this pool. If the pool is connected, the connection is not -// stale, and there is space in the cache, the connection is returned to the cache. This -// assumes that the connection has already been counted in p.conns.totalSize. -func (p *pool) put(c *connection) error { - defer p.sem.Release(1) +// checkIn returns an idle connection to the pool. If the connection is perished or the pool is +// closed, it is removed from the connection pool and closed. +func (p *pool) checkIn(conn *connection) error { + if conn == nil { + return nil + } + if conn.pool != p { + return ErrWrongPool + } + if p.monitor != nil { - var cid uint64 - var addr string - if c != nil { - cid = c.poolID - addr = c.addr.String() - } p.monitor.Event(&event.PoolEvent{ Type: event.ConnectionReturned, - ConnectionID: cid, - Address: addr, + ConnectionID: conn.poolID, + Address: conn.addr.String(), }) } - if c == nil { + return p.checkInNoEvent(conn) +} + +// checkInNoEvent returns a connection to the pool. It behaves identically to checkIn except it does +// not publish events. It is only intended for use by pool-internal functions. +func (p *pool) checkInNoEvent(conn *connection) error { + if conn == nil { return nil } - - if c.pool != p { + if conn.pool != p { return ErrWrongPool } - _ = p.conns.Put(c) + // Bump the connection idle deadline here because we're about to make the connection "available". + // The idle deadline is used to determine when a connection has reached its max idle time and + // should be closed. A connection reaches its max idle time when it has been "available" in the + // idle connections stack for more than the configured duration (maxIdleTimeMS). Set it before + // we call connectionPerished(), which checks the idle deadline, because a newly "available" + // connection should never be perished due to max idle time. + conn.bumpIdleDeadline() + + if reason, perished := connectionPerished(conn); perished { + _ = p.removeConnection(conn, reason) + go func() { + _ = p.closeConnection(conn) + }() + return nil + } + + if conn.pool.getState() == poolClosed { + _ = p.removeConnection(conn, event.ReasonPoolClosed) + go func() { + _ = p.closeConnection(conn) + }() + return nil + } + + p.idleMu.Lock() + defer p.idleMu.Unlock() + for { + w := p.idleConnWait.popFront() + if w == nil { + break + } + if w.tryDeliver(conn, nil) { + return nil + } + } + + for _, idle := range p.idleConns { + if idle == conn { + return fmt.Errorf("duplicate idle conn %p in idle connections stack", conn) + } + } + + p.idleConns = append(p.idleConns, conn) return nil } -// clear clears the pool by incrementing the generation -func (p *pool) clear(serviceID *primitive.ObjectID) { - if p.monitor != nil { +// clear marks all connections as stale by incrementing the generation number, stops all background +// goroutines, removes all requests from idleConnWait and newConnWait, and sets the pool state to +// "paused". If serviceID is nil, clear marks all connections as stale. If serviceID is not nil, +// clear marks only connections associated with the given serviceID stale (for use in load balancer +// mode). +func (p *pool) clear(err error, serviceID *primitive.ObjectID) { + if p.getState() == poolClosed { + return + } + + p.generation.clear(serviceID) + + // If serviceID is nil (i.e. not in load balancer mode), transition the pool to a paused state + // by stopping all background goroutines, clearing the wait queues, and setting the pool state + // to "paused". + sendEvent := true + if serviceID == nil { + // While holding the stateMu lock, set the pool state to "paused" if it's currently "ready", + // and set lastClearErr to the error that caused the pool to be cleared. If the pool is + // already paused, don't send another "ConnectionPoolCleared" event. + p.stateMu.Lock() + if p.state == poolPaused { + sendEvent = false + } + if p.state == poolReady { + p.state = poolPaused + } + p.lastClearErr = err + p.stateMu.Unlock() + + pcErr := poolClearedError{err: err, address: p.address} + + // Clear the idle connections wait queue. + p.idleMu.Lock() + for { + w := p.idleConnWait.popFront() + if w == nil { + break + } + w.tryDeliver(nil, pcErr) + } + p.idleMu.Unlock() + + // Clear the new connections wait queue. This effectively pauses the createConnections() + // background goroutine because newConnWait is empty and checkOut() won't insert any more + // wantConns into newConnWait until the pool is marked "ready" again. + p.createConnectionsCond.L.Lock() + for { + w := p.newConnWait.popFront() + if w == nil { + break + } + w.tryDeliver(nil, pcErr) + } + p.createConnectionsCond.L.Unlock() + } + + if sendEvent && p.monitor != nil { p.monitor.Event(&event.PoolEvent{ Type: event.PoolCleared, Address: p.address.String(), ServiceID: serviceID, }) } - p.generation.clear(serviceID) +} + +// getOrQueueForIdleConn attempts to deliver an idle connection to the given wantConn. If there is +// an idle connection in the idle connections stack, it pops an idle connection, delivers it to the +// wantConn, and returns true. If there are no idle connections in the idle connections stack, it +// adds the wantConn to the idleConnWait queue and returns false. +func (p *pool) getOrQueueForIdleConn(w *wantConn) bool { + p.idleMu.Lock() + defer p.idleMu.Unlock() + + // Try to deliver an idle connection from the idleConns stack first. + for len(p.idleConns) > 0 { + conn := p.idleConns[len(p.idleConns)-1] + p.idleConns = p.idleConns[:len(p.idleConns)-1] + + if conn == nil { + continue + } + + if reason, perished := connectionPerished(conn); perished { + _ = conn.pool.removeConnection(conn, reason) + go func() { + _ = conn.pool.closeConnection(conn) + }() + continue + } + + if !w.tryDeliver(conn, nil) { + // If we couldn't deliver the conn to w, put it back in the idleConns stack. + p.idleConns = append(p.idleConns, conn) + } + + // If we got here, we tried to deliver an idle conn to w. No matter if tryDeliver() returned + // true or false, w is no longer waiting and doesn't need to be added to any wait queues, so + // return delivered = true. + return true + } + + p.idleConnWait.cleanFront() + p.idleConnWait.pushBack(w) + return false +} + +func (p *pool) queueForNewConn(w *wantConn) { + p.createConnectionsCond.L.Lock() + defer p.createConnectionsCond.L.Unlock() + + p.newConnWait.cleanFront() + p.newConnWait.pushBack(w) + p.createConnectionsCond.Signal() +} + +func (p *pool) totalConnectionCount() int { + p.createConnectionsCond.L.Lock() + defer p.createConnectionsCond.L.Unlock() + + return len(p.conns) +} + +func (p *pool) availableConnectionCount() int { + p.idleMu.Lock() + defer p.idleMu.Unlock() + + return len(p.idleConns) +} + +// createConnections creates connections for wantConn requests on the newConnWait queue. +func (p *pool) createConnections(ctx context.Context, wg *sync.WaitGroup) { + defer wg.Done() + + // condition returns true if the createConnections() loop should continue and false if it should + // wait. Note that the condition also listens for Context cancellation, which also causes the + // loop to continue, allowing for a subsequent check to return from createConnections(). + condition := func() bool { + checkOutWaiting := p.newConnWait.len() > 0 + poolHasSpace := p.maxSize == 0 || uint64(len(p.conns)) < p.maxSize + cancelled := ctx.Err() != nil + return (checkOutWaiting && poolHasSpace) || cancelled + } + + // wait waits for there to be an available wantConn and for the pool to have space for a new + // connection. When the condition becomes true, it creates a new connection and returns the + // waiting wantConn and new connection. If the Context is cancelled or there are any + // errors, wait returns with "ok = false". + wait := func() (*wantConn, *connection, bool) { + p.createConnectionsCond.L.Lock() + defer p.createConnectionsCond.L.Unlock() + + for !condition() { + p.createConnectionsCond.Wait() + } + + if ctx.Err() != nil { + return nil, nil, false + } + + p.newConnWait.cleanFront() + w := p.newConnWait.popFront() + if w == nil { + return nil, nil, false + } + + conn := newConnection(p.address, p.connOpts...) + conn.pool = p + conn.poolID = atomic.AddUint64(&p.nextID, 1) + p.conns[conn.poolID] = conn + + return w, conn, true + } + + for ctx.Err() == nil { + w, conn, ok := wait() + if !ok { + continue + } + + if p.monitor != nil { + p.monitor.Event(&event.PoolEvent{ + Type: event.ConnectionCreated, + Address: p.address.String(), + ConnectionID: conn.poolID, + }) + } + + // Pass the createConnections context to connect to allow pool close to cancel connection + // establishment so shutdown doesn't block indefinitely if connectTimeout=0. + err := conn.connect(ctx) + if err != nil { + w.tryDeliver(nil, err) + + // If there's an error connecting the new connection, call the handshake error handler + // that implements the SDAM handshake error handling logic. This must be called after + // delivering the connection error to the waiting wantConn. If it's called before, the + // handshake error handler may clear the connection pool, leading to a different error + // message being delivered to the same waiting wantConn in idleConnWait when the wait + // queues are cleared. + if p.handshakeErrFn != nil { + p.handshakeErrFn(err, conn.generation, conn.desc.ServiceID) + } + + _ = p.removeConnection(conn, event.ReasonError) + _ = p.closeConnection(conn) + continue + } + + if p.monitor != nil { + p.monitor.Event(&event.PoolEvent{ + Type: event.ConnectionReady, + Address: p.address.String(), + ConnectionID: conn.poolID, + }) + } + + if w.tryDeliver(conn, nil) { + continue + } + + _ = p.checkInNoEvent(conn) + } +} + +func (p *pool) maintain(ctx context.Context, wg *sync.WaitGroup) { + defer wg.Done() + + ticker := time.NewTicker(p.maintainInterval) + defer ticker.Stop() + + // remove removes the *wantConn at index i from the slice and returns the new slice. The order + // of the slice is not maintained. + remove := func(arr []*wantConn, i int) []*wantConn { + end := len(arr) - 1 + arr[i], arr[end] = arr[end], arr[i] + return arr[:end] + } + + // removeNotWaiting removes any wantConns that are no longer waiting from given slice of + // wantConns. That allows maintain() to use the size of its wantConns slice as an indication of + // how many new connection requests are outstanding and subtract that from the number of + // connections to ask for when maintaining minPoolSize. + removeNotWaiting := func(arr []*wantConn) []*wantConn { + for i := len(arr) - 1; i >= 0; i-- { + w := arr[i] + if !w.waiting() { + arr = remove(arr, i) + } + } + + return arr + } + + wantConns := make([]*wantConn, 0, p.minSize) + defer func() { + for _, w := range wantConns { + w.tryDeliver(nil, ErrPoolClosed) + } + }() + + for { + select { + case <-ticker.C: + case <-p.maintainReady: + case <-ctx.Done(): + return + } + + // Only maintain the pool while it's in the "ready" state. If the pool state is not "ready", + // wait for the next tick or "ready" signal. Do all of this while holding the stateMu read + // lock to prevent a state change between checking the state and entering the wait queue. + // Not holding the stateMu read lock here may allow maintain() to request wantConns after + // clear() pauses the pool and clears the wait queue, resulting in createConnections() + // doing work while the pool is "paused". + p.stateMu.RLock() + if p.state != poolReady { + p.stateMu.RUnlock() + continue + } + + p.removePerishedConns() + + // Remove any wantConns that are no longer waiting. + wantConns = removeNotWaiting(wantConns) + + // Figure out how many more wantConns we need to satisfy minPoolSize. Assume that the + // outstanding wantConns (i.e. the ones that weren't removed from the slice) will all return + // connections when they're ready, so only add wantConns to make up the difference. Limit + // the number of connections requested to max 10 at a time to prevent overshooting + // minPoolSize in case other checkOut() calls are requesting new connections, too. + total := p.totalConnectionCount() + n := int(p.minSize) - total - len(wantConns) + if n > 10 { + n = 10 + } + + for i := 0; i < n; i++ { + w := newWantConn() + p.queueForNewConn(w) + wantConns = append(wantConns, w) + + // Start a goroutine for each new wantConn, waiting for it to be ready. + go func() { + <-w.ready + if w.conn != nil { + _ = p.checkInNoEvent(w.conn) + } + }() + } + p.stateMu.RUnlock() + } +} + +func (p *pool) removePerishedConns() { + p.idleMu.Lock() + defer p.idleMu.Unlock() + + for i := range p.idleConns { + conn := p.idleConns[i] + if conn == nil { + continue + } + + if reason, perished := connectionPerished(conn); perished { + p.idleConns[i] = nil + + _ = p.removeConnection(conn, reason) + go func() { + _ = p.closeConnection(conn) + }() + } + } + + p.idleConns = compact(p.idleConns) +} + +// compact removes any nil pointers from the slice and keeps the non-nil pointers, retaining the +// order of the non-nil pointers. +func compact(arr []*connection) []*connection { + offset := 0 + for i := range arr { + if arr[i] == nil { + continue + } + arr[offset] = arr[i] + offset++ + } + return arr[:offset] +} + +// A wantConn records state about a wanted connection (that is, an active call to checkOut). +// The conn may be gotten by creating a new connection or by finding an idle connection, or a +// cancellation may make the conn no longer wanted. These three options are racing against each +// other and use wantConn to coordinate and agree about the winning outcome. +// Based on https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/net/http/transport.go;l=1174-1240 +type wantConn struct { + ready chan struct{} + + mu sync.Mutex // Guards conn, err + conn *connection + err error +} + +func newWantConn() *wantConn { + return &wantConn{ + ready: make(chan struct{}, 1), + } +} + +// waiting reports whether w is still waiting for an answer (connection or error). +func (w *wantConn) waiting() bool { + select { + case <-w.ready: + return false + default: + return true + } +} + +// tryDeliver attempts to deliver conn, err to w and reports whether it succeeded. +func (w *wantConn) tryDeliver(conn *connection, err error) bool { + w.mu.Lock() + defer w.mu.Unlock() + + if w.conn != nil || w.err != nil { + return false + } + + w.conn = conn + w.err = err + if w.conn == nil && w.err == nil { + panic("x/mongo/driver/topology: internal error: misuse of tryDeliver") + } + close(w.ready) + return true +} + +// cancel marks w as no longer wanting a result (for example, due to cancellation). If a connection +// has been delivered already, cancel returns it with p.checkInNoEvent(). Note that the caller must +// not hold any locks on the pool while calling cancel. +func (w *wantConn) cancel(p *pool, err error) { + if err == nil { + panic("x/mongo/driver/topology: internal error: misuse of cancel") + } + + w.mu.Lock() + if w.conn == nil && w.err == nil { + close(w.ready) // catch misbehavior in future delivery + } + conn := w.conn + w.conn = nil + w.err = err + w.mu.Unlock() + + if conn != nil { + _ = p.checkInNoEvent(conn) + } +} + +// A wantConnQueue is a queue of wantConns. +// Based on https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/net/http/transport.go;l=1242-1306 +type wantConnQueue struct { + // This is a queue, not a deque. + // It is split into two stages - head[headPos:] and tail. + // popFront is trivial (headPos++) on the first stage, and + // pushBack is trivial (append) on the second stage. + // If the first stage is empty, popFront can swap the + // first and second stages to remedy the situation. + // + // This two-stage split is analogous to the use of two lists + // in Okasaki's purely functional queue but without the + // overhead of reversing the list when swapping stages. + head []*wantConn + headPos int + tail []*wantConn +} + +// len returns the number of items in the queue. +func (q *wantConnQueue) len() int { + return len(q.head) - q.headPos + len(q.tail) +} + +// pushBack adds w to the back of the queue. +func (q *wantConnQueue) pushBack(w *wantConn) { + q.tail = append(q.tail, w) +} + +// popFront removes and returns the wantConn at the front of the queue. +func (q *wantConnQueue) popFront() *wantConn { + if q.headPos >= len(q.head) { + if len(q.tail) == 0 { + return nil + } + // Pick up tail as new head, clear tail. + q.head, q.headPos, q.tail = q.tail, 0, q.head[:0] + } + w := q.head[q.headPos] + q.head[q.headPos] = nil + q.headPos++ + return w +} + +// peekFront returns the wantConn at the front of the queue without removing it. +func (q *wantConnQueue) peekFront() *wantConn { + if q.headPos < len(q.head) { + return q.head[q.headPos] + } + if len(q.tail) > 0 { + return q.tail[0] + } + return nil +} + +// cleanFront pops any wantConns that are no longer waiting from the head of the queue. +func (q *wantConnQueue) cleanFront() { + for { + w := q.peekFront() + if w == nil || w.waiting() { + return + } + q.popFront() + } } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/pool_generation_counter.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/pool_generation_counter.go index e32f0b8e..47fac2f6 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/pool_generation_counter.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/pool_generation_counter.go @@ -13,6 +13,12 @@ import ( "go.mongodb.org/mongo-driver/bson/primitive" ) +// Pool generation state constants. +const ( + generationDisconnected int64 = iota + generationConnected +) + // generationStats represents the version of a pool. It tracks the generation number as well as the number of // connections that have been created in the generation. type generationStats struct { @@ -42,11 +48,11 @@ func newPoolGenerationMap() *poolGenerationMap { } func (p *poolGenerationMap) connect() { - atomic.StoreInt64(&p.state, connected) + atomic.StoreInt64(&p.state, generationConnected) } func (p *poolGenerationMap) disconnect() { - atomic.StoreInt64(&p.state, disconnected) + atomic.StoreInt64(&p.state, generationDisconnected) } // addConnection increments the connection count for the generation associated with the given service ID and returns the @@ -102,7 +108,7 @@ func (p *poolGenerationMap) clear(serviceIDPtr *primitive.ObjectID) { func (p *poolGenerationMap) stale(serviceIDPtr *primitive.ObjectID, knownGeneration uint64) bool { // If the map has been disconnected, all connections should be considered stale to ensure that they're closed. - if atomic.LoadInt64(&p.state) == disconnected { + if atomic.LoadInt64(&p.state) == generationDisconnected { return true } @@ -127,6 +133,17 @@ func (p *poolGenerationMap) getGeneration(serviceIDPtr *primitive.ObjectID) uint return 0 } +func (p *poolGenerationMap) getNumConns(serviceIDPtr *primitive.ObjectID) uint64 { + serviceID := getServiceID(serviceIDPtr) + p.Lock() + defer p.Unlock() + + if stats, ok := p.generationMap[serviceID]; ok { + return stats.numConns + } + return 0 +} + func getServiceID(oid *primitive.ObjectID) primitive.ObjectID { if oid == nil { return primitive.NilObjectID diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/resource_pool.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/resource_pool.go deleted file mode 100644 index b7fcf310..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/resource_pool.go +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package topology - -import ( - "fmt" - "sync" - "sync/atomic" - "time" -) - -// expiredFunc is the function type used for testing whether or not resources in a resourcePool have stale. It should -// return true if the resource has stale and can be removed from the pool. -type expiredFunc func(interface{}) bool - -// closeFunc is the function type used to closeConnection resources in a resourcePool. The pool will always call this function -// asynchronously -type closeFunc func(interface{}) - -// initFunc is the function used to add a resource to the resource pool to maintain minimum size. It returns a new -// resource each time it is called. -type initFunc func() interface{} - -type resourcePoolConfig struct { - MaxSize uint64 - MinSize uint64 - MaintainInterval time.Duration - ExpiredFn expiredFunc - CloseFn closeFunc - InitFn initFunc -} - -// setup sets defaults in the rpc and checks that the given values are valid -func (rpc *resourcePoolConfig) setup() error { - if rpc.ExpiredFn == nil { - return fmt.Errorf("an ExpiredFn is required to create a resource pool") - } - if rpc.CloseFn == nil { - return fmt.Errorf("an CloseFn is required to create a resource pool") - } - if rpc.MaintainInterval == time.Duration(0) { - return fmt.Errorf("unable to have MaintainInterval time of %v", rpc.MaintainInterval) - } - return nil -} - -// resourcePoolElement is a link list element -type resourcePoolElement struct { - next, prev *resourcePoolElement - value interface{} -} - -// resourcePool is a concurrent resource pool -type resourcePool struct { - start, end *resourcePoolElement - size, minSize, maxSize, totalSize uint64 - expiredFn expiredFunc - closeFn closeFunc - initFn initFunc - maintainTimer *time.Timer - maintainInterval time.Duration - closed bool - - sync.Mutex -} - -// NewResourcePool creates a new resourcePool instance that is capped to maxSize resources. -// If maxSize is 0, the pool size will be unbounded. -func newResourcePool(config resourcePoolConfig) (*resourcePool, error) { - err := (&config).setup() - if err != nil { - return nil, err - } - rp := &resourcePool{ - minSize: config.MinSize, - maxSize: config.MaxSize, - expiredFn: config.ExpiredFn, - closeFn: config.CloseFn, - initFn: config.InitFn, - maintainInterval: config.MaintainInterval, - } - - return rp, nil -} - -func (rp *resourcePool) initialize() { - rp.Lock() - rp.maintainTimer = time.AfterFunc(rp.maintainInterval, rp.Maintain) - rp.Unlock() - - rp.Maintain() -} - -// add will add a new rpe to the pool, requires that the resource pool is locked -func (rp *resourcePool) add(e *resourcePoolElement) { - if e == nil { - e = &resourcePoolElement{ - value: rp.initFn(), - } - } - - e.next = rp.start - if rp.start != nil { - rp.start.prev = e - } - rp.start = e - if rp.end == nil { - rp.end = e - } - atomic.AddUint64(&rp.size, 1) -} - -// Get returns the first un-stale resource from the pool. If no such resource can be found, nil is returned. -func (rp *resourcePool) Get() interface{} { - rp.Lock() - defer rp.Unlock() - - for rp.start != nil { - curr := rp.start - rp.remove(curr) - if !rp.expiredFn(curr.value) { - return curr.value - } - rp.closeFn(curr.value) - rp.totalSize-- - } - return nil -} - -func (rp *resourcePool) incrementTotal() bool { - rp.Lock() - defer rp.Unlock() - if rp.maxSize > 0 && rp.totalSize >= rp.maxSize { - return false - } - rp.totalSize++ - return true -} - -func (rp *resourcePool) decrementTotal() { - rp.Lock() - defer rp.Unlock() - rp.totalSize-- -} - -func (rp *resourcePool) clearTotal() { - rp.Lock() - defer rp.Unlock() - rp.totalSize = 0 -} - -// Put puts the resource back into the pool if it will not exceed the max size of the pool. -// This assumes that v has already been accounted for by rp.totalSize -func (rp *resourcePool) Put(v interface{}) bool { - rp.Lock() - defer rp.Unlock() - if rp.expiredFn(v) { - rp.closeFn(v) - rp.totalSize-- - return false - } - - rp.add(&resourcePoolElement{value: v}) - return true -} - -// remove removes a rpe from the linked list. Requires that the pool be locked -func (rp *resourcePool) remove(e *resourcePoolElement) { - if e == nil { - return - } - - if e.prev != nil { - e.prev.next = e.next - } - if e.next != nil { - e.next.prev = e.prev - } - if e == rp.start { - rp.start = e.next - } - if e == rp.end { - rp.end = e.prev - } - atomicSubtract1Uint64(&rp.size) -} - -// Maintain puts the pool back into a state of having a correct number of resources if possible and removes all stale resources -func (rp *resourcePool) Maintain() { - rp.Lock() - defer rp.Unlock() - - if rp.closed { - return - } - - for curr := rp.end; curr != nil; curr = curr.prev { - if rp.expiredFn(curr.value) { - rp.remove(curr) - rp.closeFn(curr.value) - rp.totalSize-- - } - } - - for rp.totalSize < rp.minSize { - rp.add(nil) - rp.totalSize++ - } - - // reset the timer for the background cleanup routine - if rp.maintainTimer == nil { - rp.maintainTimer = time.AfterFunc(rp.maintainInterval, rp.Maintain) - } - if !rp.maintainTimer.Stop() { - rp.maintainTimer = time.AfterFunc(rp.maintainInterval, rp.Maintain) - return - } - rp.maintainTimer.Reset(rp.maintainInterval) -} - -// Close clears the pool and stops the background maintenance routine. -func (rp *resourcePool) Close() { - rp.Lock() - defer rp.Unlock() - - // Clear the resources in the pool. - for ; rp.start != nil; rp.start = rp.start.next { - rp.closeFn(rp.start.value) - rp.totalSize-- - } - atomic.StoreUint64(&rp.size, 0) - rp.end = nil - - // Stop the maintenance timer. If it's already fired, a call to Maintain might be waiting for the lock to be - // released, so we set closed to make that call a no-op. - rp.closed = true - _ = rp.maintainTimer.Stop() -} - -func atomicSubtract1Uint64(p *uint64) { - if p == nil || atomic.LoadUint64(p) == 0 { - return - } - - for { - expected := atomic.LoadUint64(p) - if atomic.CompareAndSwapUint64(p, expected, expected-1) { - return - } - } -} diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/rtt_monitor.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/rtt_monitor.go index 40911126..d91290e9 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/rtt_monitor.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/rtt_monitor.go @@ -8,6 +8,7 @@ package topology import ( "context" + "math" "sync" "time" @@ -17,28 +18,43 @@ import ( const ( rttAlphaValue = 0.2 + minSamples = 5 + maxSamples = 500 ) type rttConfig struct { interval time.Duration - createConnectionFn func() (*connection, error) - createOperationFn func(driver.Connection) *operation.IsMaster + minRTTWindow time.Duration // Window size to calculate minimum RTT over. + createConnectionFn func() *connection + createOperationFn func(driver.Connection) *operation.Hello } type rttMonitor struct { - sync.Mutex - conn *connection + mu sync.RWMutex // mu guards samples, offset, minRTT, averageRTT, and averageRTTSet + samples []time.Duration + offset int + minRTT time.Duration averageRTT time.Duration averageRTTSet bool - closeWg sync.WaitGroup - cfg *rttConfig - ctx context.Context - cancelFn context.CancelFunc + + closeWg sync.WaitGroup + cfg *rttConfig + ctx context.Context + cancelFn context.CancelFunc } -func newRttMonitor(cfg *rttConfig) *rttMonitor { +func newRTTMonitor(cfg *rttConfig) *rttMonitor { + if cfg.interval <= 0 { + panic("RTT monitor interval must be greater than 0") + } + ctx, cancel := context.WithCancel(context.Background()) + // Determine the number of samples we need to keep to store the minWindow of RTT durations. The + // number of samples must be between [5, 500]. + numSamples := int(math.Max(minSamples, math.Min(maxSamples, float64((cfg.minRTTWindow)/cfg.interval)))) + return &rttMonitor{ + samples: make([]time.Duration, numSamples), cfg: cfg, ctx: ctx, cancelFn: cancel, @@ -53,21 +69,6 @@ func (r *rttMonitor) connect() { func (r *rttMonitor) disconnect() { // Signal for the routine to stop. r.cancelFn() - - var conn *connection - r.Lock() - conn = r.conn - r.Unlock() - - if conn != nil { - // If the connection exists, we need to wait for it to be connected. We can ignore the error from conn.wait(). - // If the connection wasn't successfully opened, its state was set back to disconnected, so calling conn.close() - // will be a noop. - conn.closeConnectContext() - _ = conn.wait() - _ = conn.close() - } - r.closeWg.Wait() } @@ -76,77 +77,89 @@ func (r *rttMonitor) start() { ticker := time.NewTicker(r.cfg.interval) defer ticker.Stop() - for { - // The context is only cancelled in disconnect() so if there's an error on it, the monitor is shutting down. - if r.ctx.Err() != nil { - return + var conn *connection + defer func() { + if conn != nil { + // If the connection exists, we need to wait for it to be connected because + // conn.connect() and conn.close() cannot be called concurrently. If the connection + // wasn't successfully opened, its state was set back to disconnected, so calling + // conn.close() will be a no-op. + conn.closeConnectContext() + conn.wait() + _ = conn.close() } + }() - r.pingServer() + for { + conn = r.runHello(conn) select { case <-ticker.C: case <-r.ctx.Done(): - // Shutting down return } } } -// reset sets the average RTT to 0. This should only be called from the server monitor when an error occurs during a -// server check. Errors in the RTT monitor should not reset the average RTT. -func (r *rttMonitor) reset() { - r.Lock() - defer r.Unlock() +// runHello runs a "hello" operation using the provided connection, measures the duration, and adds +// the duration as an RTT sample and returns the connection used. If the provided connection is nil +// or closed, runHello tries to establish a new connection. If the "hello" operation returns an +// error, runHello closes the connection. +func (r *rttMonitor) runHello(conn *connection) *connection { + if conn == nil || conn.closed() { + conn := r.cfg.createConnectionFn() + + err := conn.connect(r.ctx) + if err != nil { + return nil + } - r.averageRTT = 0 - r.averageRTTSet = false -} + // If we just created a new connection, record the "hello" RTT from the new connection and + // return the new connection. Don't run another "hello" command this interval because it's + // now unnecessary. + r.addSample(conn.helloRTT) + return conn + } -func (r *rttMonitor) setupRttConnection() error { - conn, err := r.cfg.createConnectionFn() + start := time.Now() + err := r.cfg.createOperationFn(initConnection{conn}).Execute(r.ctx) if err != nil { - return err + // Errors from the RTT monitor do not reset the RTTs or update the topology, so we close the + // existing connection and recreate it on the next check. + _ = conn.close() + return nil } + r.addSample(time.Since(start)) - r.Lock() - r.conn = conn - r.Unlock() - - r.conn.connect(r.ctx) - return r.conn.wait() + return conn } -func (r *rttMonitor) pingServer() { - if r.conn == nil || r.conn.closed() { - if err := r.setupRttConnection(); err != nil { - return - } - - // Add the initial connection handshake time as an RTT sample. - r.addSample(r.conn.isMasterRTT) - return - } +// reset sets the average and min RTT to 0. This should only be called from the server monitor when an error +// occurs during a server check. Errors in the RTT monitor should not reset the RTTs. +func (r *rttMonitor) reset() { + r.mu.Lock() + defer r.mu.Unlock() - // We're using an already established connection. Issue an isMaster command to get a new RTT sample. - rttConn := initConnection{r.conn} - start := time.Now() - err := r.cfg.createOperationFn(rttConn).Execute(r.ctx) - if err != nil { - // Errors from the RTT monitor do not reset the average RTT or update the topology, so we close the existing - // connection and recreate it on the next check. - _ = r.conn.close() - return + for i := range r.samples { + r.samples[i] = 0 } - - r.addSample(time.Since(start)) + r.offset = 0 + r.minRTT = 0 + r.averageRTT = 0 + r.averageRTTSet = false } func (r *rttMonitor) addSample(rtt time.Duration) { // Lock for the duration of this method. We're doing compuationally inexpensive work very infrequently, so lock // contention isn't expected. - r.Lock() - defer r.Unlock() + r.mu.Lock() + defer r.mu.Unlock() + + r.samples[r.offset] = rtt + r.offset = (r.offset + 1) % len(r.samples) + // Set the minRTT as the minimum of all collected samples. Require at least 5 samples before + // setting minRTT to prevent noisy samples on startup from artificially increasing minRTT. + r.minRTT = min(r.samples, minSamples) if !r.averageRTTSet { r.averageRTT = rtt @@ -157,9 +170,38 @@ func (r *rttMonitor) addSample(rtt time.Duration) { r.averageRTT = time.Duration(rttAlphaValue*float64(rtt) + (1-rttAlphaValue)*float64(r.averageRTT)) } +// min returns the minimum value of the slice of duration samples. Zero values are not considered +// samples and are ignored. If no samples or fewer than minSamples are found in the slice, min +// returns 0. +func min(samples []time.Duration, minSamples int) time.Duration { + count := 0 + min := time.Duration(math.MaxInt64) + for _, d := range samples { + if d > 0 { + count++ + } + if d > 0 && d < min { + min = d + } + } + if count == 0 || count < minSamples { + return 0 + } + return min +} + +// getRTT returns the exponentially weighted moving average observed round-trip time. func (r *rttMonitor) getRTT() time.Duration { - r.Lock() - defer r.Unlock() + r.mu.RLock() + defer r.mu.RUnlock() return r.averageRTT } + +// getMinRTT returns the minimum observed round-trip time over the window period. +func (r *rttMonitor) getMinRTT() time.Duration { + r.mu.RLock() + defer r.mu.RUnlock() + + return r.minRTT +} diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/server.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/server.go index 2149a16b..3f8620d0 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/server.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/server.go @@ -25,6 +25,26 @@ import ( const minHeartbeatInterval = 500 * time.Millisecond +// Server state constants. +const ( + serverDisconnected int64 = iota + serverDisconnecting + serverConnected +) + +func serverStateString(state int64) string { + switch state { + case serverDisconnected: + return "Disconnected" + case serverDisconnecting: + return "Disconnecting" + case serverConnected: + return "Connected" + } + + return "" +} + var ( // ErrServerClosed occurs when an attempt to Get a connection is made after // the server has been closed. @@ -54,37 +74,18 @@ func (ss *SelectedServer) Description() description.SelectedServer { } } -// These constants represent the connection states of a server. -const ( - disconnected int64 = iota - disconnecting - connected - connecting - initialized -) - -func connectionStateString(state int64) string { - switch state { - case 0: - return "Disconnected" - case 1: - return "Disconnecting" - case 2: - return "Connected" - case 3: - return "Connecting" - case 4: - return "Initialized" - } - - return "" -} - // Server is a single server within a topology. type Server struct { - cfg *serverConfig - address address.Address - connectionstate int64 + // The following integer fields must be accessed using the atomic package and should be at the + // beginning of the struct. + // - atomic bug: https://pkg.go.dev/sync/atomic#pkg-note-BUG + // - suggested layout: https://go101.org/article/memory-layout.html + + state int64 + operationCount int64 + + cfg *serverConfig + address address.Address // connection related fields pool *pool @@ -129,11 +130,8 @@ type updateTopologyCallback func(description.Server) description.Server // ConnectServer creates a new Server and then initializes it using the // Connect method. func ConnectServer(addr address.Address, updateCallback updateTopologyCallback, topologyID primitive.ObjectID, opts ...ServerOption) (*Server, error) { - srvr, err := NewServer(addr, topologyID, opts...) - if err != nil { - return nil, err - } - err = srvr.Connect(updateCallback) + srvr := NewServer(addr, topologyID, opts...) + err := srvr.Connect(updateCallback) if err != nil { return nil, err } @@ -142,14 +140,12 @@ func ConnectServer(addr address.Address, updateCallback updateTopologyCallback, // NewServer creates a new server. The mongodb server at the address will be monitored // on an internal monitoring goroutine. -func NewServer(addr address.Address, topologyID primitive.ObjectID, opts ...ServerOption) (*Server, error) { - cfg, err := newServerConfig(opts...) - if err != nil { - return nil, err - } - +func NewServer(addr address.Address, topologyID primitive.ObjectID, opts ...ServerOption) *Server { + cfg := newServerConfig(opts...) globalCtx, globalCtxCancel := context.WithCancel(context.Background()) s := &Server{ + state: serverDisconnected, + cfg: cfg, address: addr, @@ -166,36 +162,34 @@ func NewServer(addr address.Address, topologyID primitive.ObjectID, opts ...Serv s.desc.Store(description.NewDefaultServer(addr)) rttCfg := &rttConfig{ interval: cfg.heartbeatInterval, + minRTTWindow: 5 * time.Minute, createConnectionFn: s.createConnection, createOperationFn: s.createBaseOperation, } - s.rttMonitor = newRttMonitor(rttCfg) + s.rttMonitor = newRTTMonitor(rttCfg) pc := poolConfig{ - Address: addr, - MinPoolSize: cfg.minConns, - MaxPoolSize: cfg.maxConns, - MaxIdleTime: cfg.connectionPoolMaxIdleTime, - PoolMonitor: cfg.poolMonitor, - } - - connectionOpts := make([]ConnectionOption, len(cfg.connectionOpts)) - copy(connectionOpts, cfg.connectionOpts) - connectionOpts = append(connectionOpts, withErrorHandlingCallback(s.ProcessHandshakeError)) - s.pool, err = newPool(pc, connectionOpts...) - if err != nil { - return nil, err - } - + Address: addr, + MinPoolSize: cfg.minConns, + MaxPoolSize: cfg.maxConns, + MaxConnecting: cfg.maxConnecting, + MaxIdleTime: cfg.poolMaxIdleTime, + MaintainInterval: cfg.poolMaintainInterval, + PoolMonitor: cfg.poolMonitor, + handshakeErrFn: s.ProcessHandshakeError, + } + + connectionOpts := copyConnectionOpts(cfg.connectionOpts) + s.pool = newPool(pc, connectionOpts...) s.publishServerOpeningEvent(s.address) - return s, nil + return s } // Connect initializes the Server by starting background monitoring goroutines. // This method must be called before a Server can be used. func (s *Server) Connect(updateCallback updateTopologyCallback) error { - if !atomic.CompareAndSwapInt64(&s.connectionstate, disconnected, connected) { + if !atomic.CompareAndSwapInt64(&s.state, serverDisconnected, serverConnected) { return ErrServerConnected } @@ -212,7 +206,15 @@ func (s *Server) Connect(updateCallback updateTopologyCallback) error { s.closewg.Add(1) go s.update() } - return s.pool.connect() + + // The CMAP spec describes that pools should only be marked "ready" when the server description + // is updated to something other than "Unknown". However, we maintain the previous Server + // behavior here and immediately mark the pool as ready during Connect() to simplify and speed + // up the Client startup behavior. The risk of marking a pool as ready proactively during + // Connect() is that we could attempt to create connections to a server that was configured + // erroneously until the first server check or checkOut() failure occurs, when the SDAM error + // handler would transition the Server back to "Unknown" and set the pool to "paused". + return s.pool.ready() } // Disconnect closes sockets to the server referenced by this Server. @@ -225,7 +227,7 @@ func (s *Server) Connect(updateCallback updateTopologyCallback) error { // any in flight read or write operations. If this method returns with no // errors, all connections associated with this Server have been closed. func (s *Server) Disconnect(ctx context.Context) error { - if !atomic.CompareAndSwapInt64(&s.connectionstate, connected, disconnecting) { + if !atomic.CompareAndSwapInt64(&s.state, serverConnected, serverDisconnecting) { return ErrServerClosed } @@ -240,45 +242,47 @@ func (s *Server) Disconnect(ctx context.Context) error { s.cancelCheck() s.rttMonitor.disconnect() - err := s.pool.disconnect(ctx) - if err != nil { - return err - } + s.pool.close(ctx) s.closewg.Wait() - atomic.StoreInt64(&s.connectionstate, disconnected) + atomic.StoreInt64(&s.state, serverDisconnected) return nil } // Connection gets a connection to the server. func (s *Server) Connection(ctx context.Context) (driver.Connection, error) { - - if s.pool.monitor != nil { - s.pool.monitor.Event(&event.PoolEvent{ - Type: "ConnectionCheckOutStarted", - Address: s.pool.address.String(), - }) - } - - if atomic.LoadInt64(&s.connectionstate) != connected { + if atomic.LoadInt64(&s.state) != serverConnected { return nil, ErrServerClosed } - connImpl, err := s.pool.get(ctx) + // Increment the operation count before calling checkOut to make sure that all connection + // requests are included in the operation count, including those in the wait queue. If we got an + // error instead of a connection, immediately decrement the operation count. + atomic.AddInt64(&s.operationCount, 1) + conn, err := s.pool.checkOut(ctx) if err != nil { - // The error has already been handled by connection.connect, which calls Server.ProcessHandshakeError. + atomic.AddInt64(&s.operationCount, -1) return nil, err } - return &Connection{connection: connImpl}, nil + return &Connection{ + connection: conn, + cleanupServerFn: func() { + // Decrement the operation count whenever the caller is done with the connection. Note + // that cleanupServerFn() is not called while the connection is pinned to a cursor or + // transaction, so the operation count is not decremented until the cursor is closed or + // the transaction is committed or aborted. Use an int64 instead of a uint64 to mitigate + // the impact of any possible bugs that could cause the uint64 to underflow, which would + // make the server much less selectable. + atomic.AddInt64(&s.operationCount, -1) + }, + }, nil } // ProcessHandshakeError implements SDAM error handling for errors that occur before a connection -// finishes handshaking. opCtx is the context passed to Server.Connection() and is used to determine -// whether or not an operation-scoped context deadline or cancellation was the cause of the -// handshake error; it is not used for timeout or cancellation of ProcessHandshakeError. -func (s *Server) ProcessHandshakeError(opCtx context.Context, err error, startingGenerationNumber uint64, serviceID *primitive.ObjectID) { +// finishes handshaking. +func (s *Server) ProcessHandshakeError(err error, startingGenerationNumber uint64, serviceID *primitive.ObjectID) { // Ignore the error if the server is behind a load balancer but the service ID is unknown. This indicates that the // error happened when dialing the connection or during the MongoDB handshake, so we don't know the service ID to // use for clearing the pool. @@ -295,67 +299,17 @@ func (s *Server) ProcessHandshakeError(opCtx context.Context, err error, startin return } - isCtxTimeoutOrCanceled := func(ctx context.Context) bool { - if ctx == nil { - return false - } - - if ctx.Err() != nil { - return true - } - - // In some networking functions, the deadline from the context is used to determine timeouts - // instead of the ctx.Done() chan closure. In that case, there can be a race condition - // between the networking functing returning an error and ctx.Err() returning an an error - // (i.e. the networking function returns an error caused by the context deadline, but - // ctx.Err() returns nil). If the operation-scoped context deadline was exceeded, assume - // operation-scoped context timeout caused the error. - if deadline, ok := ctx.Deadline(); ok && time.Now().After(deadline) { - return true - } - - return false - } - - isErrTimeoutOrCanceled := func(err error) bool { - for err != nil { - // Check for errors that implement the "net.Error" interface and self-report as timeout - // errors. Includes some "*net.OpError" errors and "context.DeadlineExceeded". - if netErr, ok := err.(net.Error); ok && netErr.Timeout() { - return true - } - // Check for context cancellation. Also handle the case where the cancellation error has - // been replaced by "net.errCanceled" (which isn't exported and can't be compared - // directly) by checking the error message. - if err == context.Canceled || err.Error() == "operation was canceled" { - return true - } - - wrapper, ok := err.(interface{ Unwrap() error }) - if !ok { - break - } - err = wrapper.Unwrap() - } - - return false - } - - // Ignore errors that indicate a client-side timeout occurred when the context passed into an - // operation timed out or was canceled (i.e. errors caused by an operation-scoped timeout). - // Timeouts caused by reaching connectTimeoutMS or other non-operation-scoped timeouts should - // still clear the pool. - // TODO(GODRIVER-2038): Remove this condition when connections are no longer created with an - // operation-scoped timeout. - if isCtxTimeoutOrCanceled(opCtx) && isErrTimeoutOrCanceled(wrappedConnErr) { - return - } + // Must hold the processErrorLock while updating the server description and clearing the pool. + // Not holding the lock leads to possible out-of-order processing of pool.clear() and + // pool.ready() calls from concurrent server description updates. + s.processErrorLock.Lock() + defer s.processErrorLock.Unlock() // Since the only kind of ConnectionError we receive from pool.Get will be an initialization error, we should set // the description.Server appropriately. The description should not have a TopologyVersion because the staleness // checking logic above has already determined that this description is not stale. s.updateDescription(description.NewServerFromError(s.address, wrappedConnErr, nil)) - s.pool.clear(serviceID) + s.pool.clear(err, serviceID) s.cancelCheck() } @@ -379,7 +333,7 @@ func (s *Server) SelectedDescription() description.SelectedServer { // updated server descriptions will be sent. The channel will have a buffer // size of one, and will be pre-populated with the current description. func (s *Server) Subscribe() (*ServerSubscription, error) { - if atomic.LoadInt64(&s.connectionstate) != connected { + if atomic.LoadInt64(&s.state) != serverConnected { return nil, ErrSubscribeAfterClosed } ch := make(chan description.Server, 1) @@ -422,7 +376,7 @@ func getWriteConcernErrorForProcessing(err error) (*driver.WriteConcernError, bo } wcerr := writeCmdErr.WriteConcernError - if wcerr != nil && (wcerr.NodeIsRecovering() || wcerr.NotMaster()) { + if wcerr != nil && (wcerr.NodeIsRecovering() || wcerr.NotPrimary()) { return wcerr, true } return nil, false @@ -435,6 +389,9 @@ func (s *Server) ProcessError(err error, conn driver.Connection) driver.ProcessE return driver.NoChange } + // Must hold the processErrorLock while updating the server description and clearing the pool. + // Not holding the lock leads to possible out-of-order processing of pool.clear() and + // pool.ready() calls from concurrent server description updates. s.processErrorLock.Lock() defer s.processErrorLock.Unlock() @@ -442,10 +399,10 @@ func (s *Server) ProcessError(err error, conn driver.Connection) driver.ProcessE if conn.Stale() { return driver.NoChange } - // Invalidate server description if not master or node recovering error occurs. + // Invalidate server description if not primary or node recovering error occurs. // These errors can be reported as a command error or a write concern error. desc := conn.Description() - if cerr, ok := err.(driver.Error); ok && (cerr.NodeIsRecovering() || cerr.NotMaster()) { + if cerr, ok := err.(driver.Error); ok && (cerr.NodeIsRecovering() || cerr.NotPrimary()) { // ignore stale error if desc.TopologyVersion.CompareToIncoming(cerr.TopologyVersion) >= 0 { return driver.NoChange @@ -459,8 +416,9 @@ func (s *Server) ProcessError(err error, conn driver.Connection) driver.ProcessE // If the node is shutting down or is older than 4.2, we synchronously clear the pool if cerr.NodeIsShuttingDown() || desc.WireVersion == nil || desc.WireVersion.Max < 8 { res = driver.ConnectionPoolCleared - s.pool.clear(desc.ServiceID) + s.pool.clear(err, desc.ServiceID) } + return res } if wcerr, ok := getWriteConcernErrorForProcessing(err); ok { @@ -477,7 +435,7 @@ func (s *Server) ProcessError(err error, conn driver.Connection) driver.ProcessE // If the node is shutting down or is older than 4.2, we synchronously clear the pool if wcerr.NodeIsShuttingDown() || desc.WireVersion == nil || desc.WireVersion.Max < 8 { res = driver.ConnectionPoolCleared - s.pool.clear(desc.ServiceID) + s.pool.clear(err, desc.ServiceID) } return res } @@ -499,7 +457,7 @@ func (s *Server) ProcessError(err error, conn driver.Connection) driver.ProcessE // monitoring check. The check is cancelled last to avoid a post-cancellation reconnect racing with // updateDescription. s.updateDescription(description.NewServerFromError(s.address, err, nil)) - s.pool.clear(desc.ServiceID) + s.pool.clear(err, desc.ServiceID) s.cancelCheck() return driver.ConnectionPoolCleared } @@ -577,7 +535,7 @@ func (s *Server) update() { // Perform the next check. desc, err := s.check() if err == errCheckCancelled { - if atomic.LoadInt64(&s.connectionstate) != connected { + if atomic.LoadInt64(&s.state) != serverConnected { continue } @@ -587,13 +545,18 @@ func (s *Server) update() { continue } + // Must hold the processErrorLock while updating the server description and clearing the + // pool. Not holding the lock leads to possible out-of-order processing of pool.clear() and + // pool.ready() calls from concurrent server description updates. + s.processErrorLock.Lock() s.updateDescription(desc) - if desc.LastError != nil { + if err := desc.LastError; err != nil { // Clear the pool once the description has been updated to Unknown. Pass in a nil service ID to clear // because the monitoring routine only runs for non-load balanced deployments in which servers don't return // IDs. - s.pool.clear(nil) + s.pool.clear(err, nil) } + s.processErrorLock.Unlock() // If the server supports streaming or we're already streaming, we want to move to streaming the next response // without waiting. If the server has transitioned to Unknown from a network error, we want to do another @@ -629,6 +592,18 @@ func (s *Server) updateDescription(desc description.Server) { _ = recover() }() + // Anytime we update the server description to something other than "unknown", set the pool to + // "ready". Do this before updating the description so that connections can be checked out as + // soon as the server is selectable. If the pool is already ready, this operation is a no-op. + // Note that this behavior is roughly consistent with the current Go driver behavior (connects + // to all servers, even non-data-bearing nodes) but deviates slightly from CMAP spec, which + // specifies a more restricted set of server descriptions and topologies that should mark the + // pool ready. We don't have access to the topology here, so prefer the current Go driver + // behavior for simplicity. + if desc.Kind != description.Unknown { + _ = s.pool.ready() + } + // Use the updateTopologyCallback to update the parent Topology and get the description that should be stored. callback, ok := s.updateTopologyCallback.Load().(updateTopologyCallback) if ok && callback != nil { @@ -650,9 +625,8 @@ func (s *Server) updateDescription(desc description.Server) { // createConnection creates a new connection instance but does not call connect on it. The caller must call connect // before the connection can be used for network operations. -func (s *Server) createConnection() (*connection, error) { - opts := make([]ConnectionOption, len(s.cfg.connectionOpts)) - copy(opts, s.cfg.connectionOpts) +func (s *Server) createConnection() *connection { + opts := copyConnectionOpts(s.cfg.connectionOpts) opts = append(opts, WithConnectTimeout(func(time.Duration) time.Duration { return s.cfg.heartbeatTimeout }), WithReadTimeout(func(time.Duration) time.Duration { return s.cfg.heartbeatTimeout }), @@ -660,22 +634,24 @@ func (s *Server) createConnection() (*connection, error) { // We override whatever handshaker is currently attached to the options with a basic // one because need to make sure we don't do auth. WithHandshaker(func(h Handshaker) Handshaker { - return operation.NewIsMaster().AppName(s.cfg.appname).Compressors(s.cfg.compressionOpts). + return operation.NewHello().AppName(s.cfg.appname).Compressors(s.cfg.compressionOpts). ServerAPI(s.cfg.serverAPI) }), // Override any monitors specified in options with nil to avoid monitoring heartbeats. WithMonitor(func(*event.CommandMonitor) *event.CommandMonitor { return nil }), - withPoolMonitor(func(*event.PoolMonitor) *event.PoolMonitor { return nil }), ) return newConnection(s.address, opts...) } +func copyConnectionOpts(opts []ConnectionOption) []ConnectionOption { + optsCopy := make([]ConnectionOption, len(opts)) + copy(optsCopy, opts) + return optsCopy +} + func (s *Server) setupHeartbeatConnection() error { - conn, err := s.createConnection() - if err != nil { - return err - } + conn := s.createConnection() // Take the lock when assigning the context and connection because they're accessed by cancelCheck. s.heartbeatLock.Lock() @@ -687,8 +663,7 @@ func (s *Server) setupHeartbeatConnection() error { s.conn = conn s.heartbeatLock.Unlock() - s.conn.connect(s.heartbeatCtx) - return s.conn.wait() + return s.conn.connect(s.heartbeatCtx) } // cancelCheck cancels in-progress connection dials and reads. It does not set any fields on the server. @@ -707,11 +682,11 @@ func (s *Server) cancelCheck() { return } - // If the connection exists, we need to wait for it to be connected conn.connect() and conn.close() cannot be called - // concurrently. We can ignore the error from conn.wait(). If the connection wasn't successfully opened, its state - // was set back to disconnected, so calling conn.close() will be a noop. + // If the connection exists, we need to wait for it to be connected because conn.connect() and + // conn.close() cannot be called concurrently. If the connection wasn't successfully opened, its + // state was set back to disconnected, so calling conn.close() will be a no-op. conn.closeConnectContext() - _ = conn.wait() + conn.wait() _ = conn.close() } @@ -719,9 +694,9 @@ func (s *Server) checkWasCancelled() bool { return s.heartbeatCtx.Err() != nil } -func (s *Server) createBaseOperation(conn driver.Connection) *operation.IsMaster { +func (s *Server) createBaseOperation(conn driver.Connection) *operation.Hello { return operation. - NewIsMaster(). + NewHello(). ClusterClock(s.cfg.clock). Deployment(driver.SingleConnectionDeployment{conn}). ServerAPI(s.cfg.serverAPI) @@ -739,9 +714,8 @@ func (s *Server) check() (description.Server, error) { err = s.setupHeartbeatConnection() if err == nil { // Use the description from the connection handshake as the value for this check. - s.rttMonitor.addSample(s.conn.isMasterRTT) + s.rttMonitor.addSample(s.conn.helloRTT) descPtr = &s.conn.desc - durationNanos = s.conn.isMasterRTT.Nanoseconds() } } @@ -762,7 +736,7 @@ func (s *Server) check() (description.Server, error) { err = baseOperation.StreamResponse(s.heartbeatCtx, heartbeatConn) case streamable: // The server supports the streamable protocol. Set the socket timeout to - // connectTimeoutMS+heartbeatFrequencyMS and execute an awaitable isMaster request. Set conn.canStream so + // connectTimeoutMS+heartbeatFrequencyMS and execute an awaitable hello request. Set conn.canStream so // the wire message will advertise streaming support to the server. // Calculation for maxAwaitTimeMS is taken from time.Duration.Milliseconds (added in Go 1.13). @@ -840,17 +814,27 @@ func extractTopologyVersion(err error) *description.TopologyVersion { return nil } +// MinRTT returns the minimum round-trip time to the server observed over the last 5 minutes. +func (s *Server) MinRTT() time.Duration { + return s.rttMonitor.getMinRTT() +} + +// OperationCount returns the current number of in-progress operations for this server. +func (s *Server) OperationCount() int64 { + return atomic.LoadInt64(&s.operationCount) +} + // String implements the Stringer interface. func (s *Server) String() string { desc := s.Description() - connState := atomic.LoadInt64(&s.connectionstate) + state := atomic.LoadInt64(&s.state) str := fmt.Sprintf("Addr: %s, Type: %s, State: %s", - s.address, desc.Kind, connectionStateString(connState)) + s.address, desc.Kind, serverStateString(state)) if len(desc.Tags) != 0 { str += fmt.Sprintf(", Tag sets: %s", desc.Tags) } - if connState == connected { - str += fmt.Sprintf(", Average RTT: %d", desc.AverageRTT) + if state == serverConnected { + str += fmt.Sprintf(", Average RTT: %s, Min RTT: %s", desc.AverageRTT, s.MinRTT()) } if desc.LastError != nil { str += fmt.Sprintf(", Last error: %s", desc.LastError) @@ -889,17 +873,21 @@ func (ss *ServerSubscription) Unsubscribe() error { // publishes a ServerOpeningEvent to indicate the server is being initialized func (s *Server) publishServerOpeningEvent(addr address.Address) { + if s == nil { + return + } + serverOpening := &event.ServerOpeningEvent{ Address: addr, TopologyID: s.topologyID, } - if s != nil && s.cfg.serverMonitor != nil && s.cfg.serverMonitor.ServerOpening != nil { + if s.cfg.serverMonitor != nil && s.cfg.serverMonitor.ServerOpening != nil { s.cfg.serverMonitor.ServerOpening(serverOpening) } } -// publishes a ServerHeartbeatStartedEvent to indicate an ismaster command has started +// publishes a ServerHeartbeatStartedEvent to indicate a hello command has started func (s *Server) publishServerHeartbeatStartedEvent(connectionID string, await bool) { serverHeartbeatStarted := &event.ServerHeartbeatStartedEvent{ ConnectionID: connectionID, @@ -911,7 +899,7 @@ func (s *Server) publishServerHeartbeatStartedEvent(connectionID string, await b } } -// publishes a ServerHeartbeatSucceededEvent to indicate ismaster has succeeded +// publishes a ServerHeartbeatSucceededEvent to indicate hello has succeeded func (s *Server) publishServerHeartbeatSucceededEvent(connectionID string, durationNanos int64, desc description.Server, @@ -928,7 +916,7 @@ func (s *Server) publishServerHeartbeatSucceededEvent(connectionID string, } } -// publishes a ServerHeartbeatFailedEvent to indicate ismaster has failed +// publishes a ServerHeartbeatFailedEvent to indicate hello has failed func (s *Server) publishServerHeartbeatFailedEvent(connectionID string, durationNanos int64, err error, diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/server_options.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/server_options.go index 1b82f192..a4b7dcc2 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/server_options.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/server_options.go @@ -19,24 +19,28 @@ import ( var defaultRegistry = bson.NewRegistryBuilder().Build() type serverConfig struct { - clock *session.ClusterClock - compressionOpts []string - connectionOpts []ConnectionOption - appname string - heartbeatInterval time.Duration - heartbeatTimeout time.Duration - maxConns uint64 - minConns uint64 - poolMonitor *event.PoolMonitor - serverMonitor *event.ServerMonitor - connectionPoolMaxIdleTime time.Duration - registry *bsoncodec.Registry - monitoringDisabled bool - serverAPI *driver.ServerAPIOptions - loadBalanced bool -} - -func newServerConfig(opts ...ServerOption) (*serverConfig, error) { + clock *session.ClusterClock + compressionOpts []string + connectionOpts []ConnectionOption + appname string + heartbeatInterval time.Duration + heartbeatTimeout time.Duration + serverMonitor *event.ServerMonitor + registry *bsoncodec.Registry + monitoringDisabled bool + serverAPI *driver.ServerAPIOptions + loadBalanced bool + + // Connection pool options. + maxConns uint64 + minConns uint64 + maxConnecting uint64 + poolMonitor *event.PoolMonitor + poolMaxIdleTime time.Duration + poolMaintainInterval time.Duration +} + +func newServerConfig(opts ...ServerOption) *serverConfig { cfg := &serverConfig{ heartbeatInterval: 10 * time.Second, heartbeatTimeout: 10 * time.Second, @@ -45,72 +49,65 @@ func newServerConfig(opts ...ServerOption) (*serverConfig, error) { } for _, opt := range opts { - err := opt(cfg) - if err != nil { - return nil, err + if opt == nil { + continue } + opt(cfg) } - return cfg, nil + return cfg } // ServerOption configures a server. -type ServerOption func(*serverConfig) error +type ServerOption func(*serverConfig) func withMonitoringDisabled(fn func(bool) bool) ServerOption { - return func(cfg *serverConfig) error { + return func(cfg *serverConfig) { cfg.monitoringDisabled = fn(cfg.monitoringDisabled) - return nil } } // WithConnectionOptions configures the server's connections. func WithConnectionOptions(fn func(...ConnectionOption) []ConnectionOption) ServerOption { - return func(cfg *serverConfig) error { + return func(cfg *serverConfig) { cfg.connectionOpts = fn(cfg.connectionOpts...) - return nil } } // WithCompressionOptions configures the server's compressors. func WithCompressionOptions(fn func(...string) []string) ServerOption { - return func(cfg *serverConfig) error { + return func(cfg *serverConfig) { cfg.compressionOpts = fn(cfg.compressionOpts...) - return nil } } // WithServerAppName configures the server's application name. func WithServerAppName(fn func(string) string) ServerOption { - return func(cfg *serverConfig) error { + return func(cfg *serverConfig) { cfg.appname = fn(cfg.appname) - return nil } } // WithHeartbeatInterval configures a server's heartbeat interval. func WithHeartbeatInterval(fn func(time.Duration) time.Duration) ServerOption { - return func(cfg *serverConfig) error { + return func(cfg *serverConfig) { cfg.heartbeatInterval = fn(cfg.heartbeatInterval) - return nil } } // WithHeartbeatTimeout configures how long to wait for a heartbeat socket to // connection. func WithHeartbeatTimeout(fn func(time.Duration) time.Duration) ServerOption { - return func(cfg *serverConfig) error { + return func(cfg *serverConfig) { cfg.heartbeatTimeout = fn(cfg.heartbeatTimeout) - return nil } } // WithMaxConnections configures the maximum number of connections to allow for -// a given server. If max is 0, then the default will be math.MaxInt64. +// a given server. If max is 0, then maximum connection pool size is not limited. func WithMaxConnections(fn func(uint64) uint64) ServerOption { - return func(cfg *serverConfig) error { + return func(cfg *serverConfig) { cfg.maxConns = fn(cfg.maxConns) - return nil } } @@ -118,9 +115,17 @@ func WithMaxConnections(fn func(uint64) uint64) ServerOption { // a given server. If min is 0, then there is no lower limit to the number of // connections. func WithMinConnections(fn func(uint64) uint64) ServerOption { - return func(cfg *serverConfig) error { + return func(cfg *serverConfig) { cfg.minConns = fn(cfg.minConns) - return nil + } +} + +// WithMaxConnecting configures the maximum number of connections a connection +// pool may establish simultaneously. If maxConnecting is 0, the default value +// of 2 is used. +func WithMaxConnecting(fn func(uint64) uint64) ServerOption { + return func(cfg *serverConfig) { + cfg.maxConnecting = fn(cfg.maxConnecting) } } @@ -128,57 +133,58 @@ func WithMinConnections(fn func(uint64) uint64) ServerOption { // before being removed. If connectionPoolMaxIdleTime is 0, then no idle time is set and connections will not be removed // because of their age func WithConnectionPoolMaxIdleTime(fn func(time.Duration) time.Duration) ServerOption { - return func(cfg *serverConfig) error { - cfg.connectionPoolMaxIdleTime = fn(cfg.connectionPoolMaxIdleTime) - return nil + return func(cfg *serverConfig) { + cfg.poolMaxIdleTime = fn(cfg.poolMaxIdleTime) + } +} + +// WithConnectionPoolMaintainInterval configures the interval that the background connection pool +// maintenance goroutine runs. +func WithConnectionPoolMaintainInterval(fn func(time.Duration) time.Duration) ServerOption { + return func(cfg *serverConfig) { + cfg.poolMaintainInterval = fn(cfg.poolMaintainInterval) } } // WithConnectionPoolMonitor configures the monitor for all connection pool actions func WithConnectionPoolMonitor(fn func(*event.PoolMonitor) *event.PoolMonitor) ServerOption { - return func(cfg *serverConfig) error { + return func(cfg *serverConfig) { cfg.poolMonitor = fn(cfg.poolMonitor) - return nil } } // WithServerMonitor configures the monitor for all SDAM events for a server func WithServerMonitor(fn func(*event.ServerMonitor) *event.ServerMonitor) ServerOption { - return func(cfg *serverConfig) error { + return func(cfg *serverConfig) { cfg.serverMonitor = fn(cfg.serverMonitor) - return nil } } // WithClock configures the ClusterClock for the server to use. func WithClock(fn func(clock *session.ClusterClock) *session.ClusterClock) ServerOption { - return func(cfg *serverConfig) error { + return func(cfg *serverConfig) { cfg.clock = fn(cfg.clock) - return nil } } // WithRegistry configures the registry for the server to use when creating // cursors. func WithRegistry(fn func(*bsoncodec.Registry) *bsoncodec.Registry) ServerOption { - return func(cfg *serverConfig) error { + return func(cfg *serverConfig) { cfg.registry = fn(cfg.registry) - return nil } } // WithServerAPI configures the server API options for the server to use. func WithServerAPI(fn func(serverAPI *driver.ServerAPIOptions) *driver.ServerAPIOptions) ServerOption { - return func(cfg *serverConfig) error { + return func(cfg *serverConfig) { cfg.serverAPI = fn(cfg.serverAPI) - return nil } } // WithServerLoadBalanced specifies whether or not the server is behind a load balancer. func WithServerLoadBalanced(fn func(bool) bool) ServerOption { - return func(cfg *serverConfig) error { + return func(cfg *serverConfig) { cfg.loadBalanced = fn(cfg.loadBalanced) - return nil } } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/tls_connection_source_1_16.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/tls_connection_source_1_16.go new file mode 100644 index 00000000..387f2ec0 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/tls_connection_source_1_16.go @@ -0,0 +1,58 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + +//go:build !go1.17 +// +build !go1.17 + +package topology + +import ( + "context" + "crypto/tls" + "net" +) + +type tlsConn interface { + net.Conn + + // Only require Handshake on the interface for Go 1.16 and less. + Handshake() error + ConnectionState() tls.ConnectionState +} + +var _ tlsConn = (*tls.Conn)(nil) + +type tlsConnectionSource interface { + Client(net.Conn, *tls.Config) tlsConn +} + +type tlsConnectionSourceFn func(net.Conn, *tls.Config) tlsConn + +var _ tlsConnectionSource = (tlsConnectionSourceFn)(nil) + +func (t tlsConnectionSourceFn) Client(nc net.Conn, cfg *tls.Config) tlsConn { + return t(nc, cfg) +} + +var defaultTLSConnectionSource tlsConnectionSourceFn = func(nc net.Conn, cfg *tls.Config) tlsConn { + return tls.Client(nc, cfg) +} + +// clientHandshake will perform a handshake with a goroutine and wait for its completion on Go 1.16 and less +// when HandshakeContext is not available. +func clientHandshake(ctx context.Context, client tlsConn) error { + errChan := make(chan error, 1) + go func() { + errChan <- client.Handshake() + }() + + select { + case err := <-errChan: + return err + case <-ctx.Done(): + return ctx.Err() + } +} diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/tls_connection_source.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/tls_connection_source_1_17.go similarity index 66% rename from vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/tls_connection_source.go rename to vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/tls_connection_source_1_17.go index 718a9abb..c9822e06 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/tls_connection_source.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/tls_connection_source_1_17.go @@ -1,19 +1,25 @@ -// Copyright (C) MongoDB, Inc. 2017-present. +// Copyright (C) MongoDB, Inc. 2022-present. // // Licensed under the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +//go:build go1.17 +// +build go1.17 + package topology import ( + "context" "crypto/tls" "net" ) type tlsConn interface { net.Conn - Handshake() error + + // Require HandshakeContext on the interface for Go 1.17 and higher. + HandshakeContext(ctx context.Context) error ConnectionState() tls.ConnectionState } @@ -34,3 +40,8 @@ func (t tlsConnectionSourceFn) Client(nc net.Conn, cfg *tls.Config) tlsConn { var defaultTLSConnectionSource tlsConnectionSourceFn = func(nc net.Conn, cfg *tls.Config) tlsConn { return tls.Client(nc, cfg) } + +// clientHandshake will perform a handshake on Go 1.17 and higher with HandshakeContext. +func clientHandshake(ctx context.Context, client tlsConn) error { + return client.HandshakeContext(ctx) +} diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology.go index 24afb1da..97ec5e52 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology.go @@ -13,22 +13,30 @@ package topology // import "go.mongodb.org/mongo-driver/x/mongo/driver/topology" import ( "context" "errors" + "fmt" "math/rand" "strings" "sync" "sync/atomic" "time" - "fmt" - "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/event" + "go.mongodb.org/mongo-driver/internal/randutil" "go.mongodb.org/mongo-driver/mongo/address" "go.mongodb.org/mongo-driver/mongo/description" "go.mongodb.org/mongo-driver/x/mongo/driver" "go.mongodb.org/mongo-driver/x/mongo/driver/dns" ) +// Topology state constants. +const ( + topologyDisconnected int64 = iota + topologyDisconnecting + topologyConnected + topologyConnecting +) + // ErrSubscribeAfterClosed is returned when a user attempts to subscribe to a // closed Server or Topology. var ErrSubscribeAfterClosed = errors.New("cannot subscribe after closeConnection") @@ -48,6 +56,9 @@ var ErrServerSelectionTimeout = errors.New("server selection timeout") // MonitorMode represents the way in which a server is monitored. type MonitorMode uint8 +// random is a package-global pseudo-random number generator. +var random = randutil.NewLockedRand(rand.NewSource(randutil.CryptoSeed())) + // These constants are the available monitoring modes. const ( AutomaticMode MonitorMode = iota @@ -56,7 +67,7 @@ const ( // Topology represents a MongoDB deployment. type Topology struct { - connectionstate int64 + state int64 cfg *config @@ -144,7 +155,7 @@ func New(opts ...Option) (*Topology, error) { // Connect initializes a Topology and starts the monitoring process. This function // must be called to properly monitor the topology. func (t *Topology) Connect() error { - if !atomic.CompareAndSwapInt64(&t.connectionstate, disconnected, connecting) { + if !atomic.CompareAndSwapInt64(&t.state, topologyDisconnected, topologyConnecting) { return ErrTopologyConnected } @@ -226,14 +237,14 @@ func (t *Topology) Connect() error { t.subscriptionsClosed = false // explicitly set in case topology was disconnected and then reconnected - atomic.StoreInt64(&t.connectionstate, connected) + atomic.StoreInt64(&t.state, topologyConnected) return nil } // Disconnect closes the topology. It stops the monitoring thread and // closes all open subscriptions. func (t *Topology) Disconnect(ctx context.Context) error { - if !atomic.CompareAndSwapInt64(&t.connectionstate, connected, disconnecting) { + if !atomic.CompareAndSwapInt64(&t.state, topologyConnected, topologyDisconnecting) { return ErrTopologyClosed } @@ -265,7 +276,7 @@ func (t *Topology) Disconnect(ctx context.Context) error { t.desc.Store(description.Topology{}) - atomic.StoreInt64(&t.connectionstate, disconnected) + atomic.StoreInt64(&t.state, topologyDisconnected) t.publishTopologyClosedEvent() return nil } @@ -287,7 +298,7 @@ func (t *Topology) Kind() description.TopologyKind { return t.Description().Kind // and will be pre-populated with the current description.Topology. // Subscribe implements the driver.Subscriber interface. func (t *Topology) Subscribe() (*driver.Subscription, error) { - if atomic.LoadInt64(&t.connectionstate) != connected { + if atomic.LoadInt64(&t.state) != topologyConnected { return nil, errors.New("cannot subscribe to Topology that is not connected") } ch := make(chan description.Topology, 1) @@ -335,7 +346,7 @@ func (t *Topology) Unsubscribe(sub *driver.Subscription) error { // RequestImmediateCheck will send heartbeats to all the servers in the // topology right away, instead of waiting for the heartbeat timeout. func (t *Topology) RequestImmediateCheck() { - if atomic.LoadInt64(&t.connectionstate) != connected { + if atomic.LoadInt64(&t.state) != topologyConnected { return } t.serversLock.Lock() @@ -349,7 +360,7 @@ func (t *Topology) RequestImmediateCheck() { // server selection spec, and will time out after severSelectionTimeout or when the // parent context is done. func (t *Topology) SelectServer(ctx context.Context, ss description.ServerSelector) (driver.Server, error) { - if atomic.LoadInt64(&t.connectionstate) != connected { + if atomic.LoadInt64(&t.state) != topologyConnected { return nil, ErrTopologyClosed } var ssTimeoutCh <-chan time.Time @@ -395,26 +406,77 @@ func (t *Topology) SelectServer(ctx context.Context, ss description.ServerSelect continue } - selected := suitable[rand.Intn(len(suitable))] - selectedS, err := t.FindServer(selected) - switch { - case err != nil: + // If there's only one suitable server description, try to find the associated server and + // return it. This is an optimization primarily for standalone and load-balanced deployments. + if len(suitable) == 1 { + server, err := t.FindServer(suitable[0]) + if err != nil { + return nil, err + } + if server == nil { + continue + } + return server, nil + } + + // Randomly select 2 suitable server descriptions and find servers for them. We select two + // so we can pick the one with the one with fewer in-progress operations below. + desc1, desc2 := pick2(suitable) + server1, err := t.FindServer(desc1) + if err != nil { return nil, err - case selectedS != nil: - return selectedS, nil - default: - // We don't have an actual server for the provided description. - // This could happen for a number of reasons, including that the - // server has since stopped being a part of this topology, or that - // the server selector returned no suitable servers. } + server2, err := t.FindServer(desc2) + if err != nil { + return nil, err + } + + // If we don't have an actual server for one or both of the provided descriptions, either + // return the one server we have, or try again if they're both nil. This could happen for a + // number of reasons, including that the server has since stopped being a part of this + // topology. + if server1 == nil || server2 == nil { + if server1 == nil && server2 == nil { + continue + } + if server1 != nil { + return server1, nil + } + return server2, nil + } + + // Of the two randomly selected suitable servers, pick the one with fewer in-use connections. + // We use in-use connections as an analog for in-progress operations because they are almost + // always the same value for a given server. + if server1.OperationCount() < server2.OperationCount() { + return server1, nil + } + return server2, nil } } +// pick2 returns 2 random server descriptions from the input slice of server descriptions, +// guaranteeing that the same element from the slice is not picked twice. The order of server +// descriptions in the input slice may be modified. If fewer than 2 server descriptions are +// provided, pick2 will panic. +func pick2(ds []description.Server) (description.Server, description.Server) { + // Select a random index from the input slice and keep the server description from that index. + idx := random.Intn(len(ds)) + s1 := ds[idx] + + // Swap the selected index to the end and reslice to remove it so we don't pick the same server + // description twice. + ds[idx], ds[len(ds)-1] = ds[len(ds)-1], ds[idx] + ds = ds[:len(ds)-1] + + // Select another random index from the input slice and return both selected server descriptions. + return s1, ds[random.Intn(len(ds))] +} + // FindServer will attempt to find a server that fits the given server description. // This method will return nil, nil if a matching server could not be found. func (t *Topology) FindServer(selected description.Server) (*SelectedServer, error) { - if atomic.LoadInt64(&t.connectionstate) != connected { + if atomic.LoadInt64(&t.state) != topologyConnected { return nil, ErrTopologyClosed } t.serversLock.Lock() @@ -494,7 +556,7 @@ func (t *Topology) selectServerFromDescription(desc description.Topology, func (t *Topology) pollSRVRecords() { defer t.pollingwg.Done() - serverConfig, _ := newServerConfig(t.cfg.serverOpts...) + serverConfig := newServerConfig(t.cfg.serverOpts...) heartbeatInterval := serverConfig.heartbeatInterval pollTicker := time.NewTicker(t.rescanSRVInterval) @@ -527,7 +589,7 @@ func (t *Topology) pollSRVRecords() { break } - parsedHosts, err := t.dnsResolver.ParseHosts(hosts, false) + parsedHosts, err := t.dnsResolver.ParseHosts(hosts, t.cfg.srvServiceName, false) // DNS problem or no verified hosts returned if err != nil || len(parsedHosts) == 0 { if !t.pollHeartbeatTime.Load().(bool) { @@ -581,11 +643,25 @@ func (t *Topology) processSRVResults(parsedHosts []string) bool { t.fsm.removeServerByAddr(addr) t.publishServerClosedEvent(s.address) } + + // Now that we've removed all the hosts that disappeared from the SRV record, we need to add any + // new hosts added to the SRV record. If adding all of the new hosts would increase the number + // of servers past srvMaxHosts, shuffle the list of added hosts. + if t.cfg.srvMaxHosts > 0 && len(t.servers)+len(diff.Added) > t.cfg.srvMaxHosts { + random.Shuffle(len(diff.Added), func(i, j int) { + diff.Added[i], diff.Added[j] = diff.Added[j], diff.Added[i] + }) + } + // Add all added hosts until the number of servers reaches srvMaxHosts. for _, a := range diff.Added { + if t.cfg.srvMaxHosts > 0 && len(t.servers) >= t.cfg.srvMaxHosts { + break + } addr := address.Address(a).Canonicalize() _ = t.addServer(addr) t.fsm.addServer(addr) } + //store new description newDesc := description.Topology{ Kind: t.fsm.Kind, @@ -610,7 +686,6 @@ func (t *Topology) processSRVResults(parsedHosts []string) bool { t.subLock.Unlock() return true - } // apply updates the Topology and its underlying FSM based on the provided server description and returns the server @@ -631,11 +706,7 @@ func (t *Topology) apply(ctx context.Context, desc description.Server) descripti } var current description.Topology - var err error - current, desc, err = t.fsm.apply(desc) - if err != nil { - return desc - } + current, desc = t.fsm.apply(desc) if !oldDesc.Equal(desc) { t.publishServerDescriptionChangedEvent(oldDesc, desc) diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology_options.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology_options.go index fa74764e..b405739c 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology_options.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology_options.go @@ -36,6 +36,8 @@ type config struct { uri string serverSelectionTimeout time.Duration serverMonitor *event.ServerMonitor + srvMaxHosts int + srvServiceName string loadBalanced bool } @@ -46,6 +48,9 @@ func newConfig(opts ...Option) (*config, error) { } for _, opt := range opts { + if opt == nil { + continue + } err := opt(cfg) if err != nil { return nil, err @@ -188,6 +193,7 @@ func WithConnString(fn func(connstring.ConnString) connstring.ConnString) Option AppName: cs.AppName, Authenticator: authenticator, Compressors: cs.Compressors, + LoadBalanced: cs.LoadBalancedSet && cs.LoadBalanced, } if cs.AuthMechanism == "" { // Required for SASL mechanism negotiation during handshake @@ -198,7 +204,10 @@ func WithConnString(fn func(connstring.ConnString) connstring.ConnString) Option } else { // We need to add a non-auth Handshaker to the connection options connOpts = append(connOpts, WithHandshaker(func(h driver.Handshaker) driver.Handshaker { - return operation.NewIsMaster().AppName(cs.AppName).Compressors(cs.Compressors) + return operation.NewHello(). + AppName(cs.AppName). + Compressors(cs.Compressors). + LoadBalanced(cs.LoadBalancedSet && cs.LoadBalanced) })) } @@ -304,6 +313,30 @@ func WithURI(fn func(string) string) Option { } } +// WithLoadBalanced specifies whether or not the cluster is behind a load balancer. +func WithLoadBalanced(fn func(bool) bool) Option { + return func(cfg *config) error { + cfg.loadBalanced = fn(cfg.loadBalanced) + return nil + } +} + +// WithSRVMaxHosts specifies the SRV host limit that was used to create the topology. +func WithSRVMaxHosts(fn func(int) int) Option { + return func(cfg *config) error { + cfg.srvMaxHosts = fn(cfg.srvMaxHosts) + return nil + } +} + +// WithSRVServiceName specifies the SRV service name that was used to create the topology. +func WithSRVServiceName(fn func(string) string) Option { + return func(cfg *config) error { + cfg.srvServiceName = fn(cfg.srvServiceName) + return nil + } +} + // addCACertFromFile adds a root CA certificate to the configuration given a path // to the containing file. func addCACertFromFile(cfg *tls.Config, file string) error { @@ -335,7 +368,7 @@ func loadCert(data []byte) ([]byte, error) { var certBlock *pem.Block for certBlock == nil { - if data == nil || len(data) == 0 { + if len(data) == 0 { return nil, errors.New(".pem file must have both a CERTIFICATE and an RSA PRIVATE KEY section") } @@ -420,13 +453,5 @@ func addClientCertFromFile(cfg *tls.Config, clientFile, keyPasswd string) (strin return "", err } - return x509CertSubject(crt), nil -} - -// WithLoadBalanced specifies whether or not the cluster is behind a load balancer. -func WithLoadBalanced(fn func(bool) bool) Option { - return func(cfg *config) error { - cfg.loadBalanced = fn(cfg.loadBalanced) - return nil - } + return crt.Subject.String(), nil } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology_options_1_10.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology_options_1_10.go deleted file mode 100644 index 04c4b897..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology_options_1_10.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build go1.10 - -package topology - -import "crypto/x509" - -func x509CertSubject(cert *x509.Certificate) string { - return cert.Subject.String() -} diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology_options_1_9.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology_options_1_9.go deleted file mode 100644 index b3ad273f..00000000 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology_options_1_9.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build !go1.10 - -package topology - -import ( - "crypto/x509" -) - -// We don't support version less then 1.10, but Evergreen needs to be able to compile the driver -// using version 1.8. -func x509CertSubject(cert *x509.Certificate) string { - return "" -} diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/uuid/uuid.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/uuid/uuid.go index 83a67850..09783873 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/uuid/uuid.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/uuid/uuid.go @@ -7,21 +7,26 @@ package uuid // import "go.mongodb.org/mongo-driver/x/mongo/driver/uuid" import ( - "bytes" - "crypto/rand" "io" + "math/rand" + + "go.mongodb.org/mongo-driver/internal/randutil" ) // UUID represents a UUID. type UUID [16]byte -var rander = rand.Reader +// A source is a UUID generator that reads random values from a randutil.LockedRand. +// It is safe to use from multiple goroutines. +type source struct { + random *randutil.LockedRand +} -// New generates a new uuid. -func New() (UUID, error) { +// new returns a random UUIDv4 with bytes read from the source's random number generator. +func (s *source) new() (UUID, error) { var uuid [16]byte - _, err := io.ReadFull(rander, uuid[:]) + _, err := io.ReadFull(s.random, uuid[:]) if err != nil { return [16]byte{}, err } @@ -31,7 +36,27 @@ func New() (UUID, error) { return uuid, nil } +// newGlobalSource returns a source that uses a "math/rand" pseudo-random number generator seeded +// with a cryptographically-secure random number. It is intended to be used to initialize the +// package-global UUID generator. +func newGlobalSource() *source { + return &source{ + random: randutil.NewLockedRand(rand.NewSource(randutil.CryptoSeed())), + } +} + +// globalSource is a package-global pseudo-random UUID generator. +var globalSource = newGlobalSource() + +// New returns a random UUIDv4. It uses a "math/rand" pseudo-random number generator seeded with a +// cryptographically-secure random number at package initialization. +// +// New should not be used to generate cryptographically-secure random UUIDs. +func New() (UUID, error) { + return globalSource.new() +} + // Equal returns true if two UUIDs are equal. func Equal(a, b UUID) bool { - return bytes.Equal([]byte(a[:]), []byte(b[:])) + return a == b } diff --git a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/wiremessage/wiremessage.go b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/wiremessage/wiremessage.go index f00e47b9..47f086e8 100644 --- a/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/wiremessage/wiremessage.go +++ b/vendor/go.mongodb.org/mongo-driver/x/mongo/driver/wiremessage/wiremessage.go @@ -78,7 +78,7 @@ type QueryFlag int32 const ( _ QueryFlag = 1 << iota TailableCursor - SlaveOK + SecondaryOK OplogReplay NoCursorTimeout AwaitData @@ -92,8 +92,8 @@ func (qf QueryFlag) String() string { if qf&TailableCursor == TailableCursor { strs = append(strs, "TailableCursor") } - if qf&SlaveOK == SlaveOK { - strs = append(strs, "SlaveOK") + if qf&SecondaryOK == SecondaryOK { + strs = append(strs, "SecondaryOK") } if qf&OplogReplay == OplogReplay { strs = append(strs, "OplogReplay") @@ -391,7 +391,7 @@ func ReadMsgSectionRawDocumentSequence(src []byte) (identifier string, data []by return "", nil, rem, false } - // After these assignments, rem will be the data containing the identifer string + the document sequence bytes and + // After these assignments, rem will be the data containing the identifier string + the document sequence bytes and // rest will be the rest of the wire message after this document sequence. rem, rest := rem[:length-4], rem[length-4:] diff --git a/vendor/golang.org/x/sync/semaphore/semaphore.go b/vendor/golang.org/x/sync/semaphore/semaphore.go deleted file mode 100644 index 30f632c5..00000000 --- a/vendor/golang.org/x/sync/semaphore/semaphore.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package semaphore provides a weighted semaphore implementation. -package semaphore // import "golang.org/x/sync/semaphore" - -import ( - "container/list" - "context" - "sync" -) - -type waiter struct { - n int64 - ready chan<- struct{} // Closed when semaphore acquired. -} - -// NewWeighted creates a new weighted semaphore with the given -// maximum combined weight for concurrent access. -func NewWeighted(n int64) *Weighted { - w := &Weighted{size: n} - return w -} - -// Weighted provides a way to bound concurrent access to a resource. -// The callers can request access with a given weight. -type Weighted struct { - size int64 - cur int64 - mu sync.Mutex - waiters list.List -} - -// Acquire acquires the semaphore with a weight of n, blocking until resources -// are available or ctx is done. On success, returns nil. On failure, returns -// ctx.Err() and leaves the semaphore unchanged. -// -// If ctx is already done, Acquire may still succeed without blocking. -func (s *Weighted) Acquire(ctx context.Context, n int64) error { - s.mu.Lock() - if s.size-s.cur >= n && s.waiters.Len() == 0 { - s.cur += n - s.mu.Unlock() - return nil - } - - if n > s.size { - // Don't make other Acquire calls block on one that's doomed to fail. - s.mu.Unlock() - <-ctx.Done() - return ctx.Err() - } - - ready := make(chan struct{}) - w := waiter{n: n, ready: ready} - elem := s.waiters.PushBack(w) - s.mu.Unlock() - - select { - case <-ctx.Done(): - err := ctx.Err() - s.mu.Lock() - select { - case <-ready: - // Acquired the semaphore after we were canceled. Rather than trying to - // fix up the queue, just pretend we didn't notice the cancelation. - err = nil - default: - isFront := s.waiters.Front() == elem - s.waiters.Remove(elem) - // If we're at the front and there're extra tokens left, notify other waiters. - if isFront && s.size > s.cur { - s.notifyWaiters() - } - } - s.mu.Unlock() - return err - - case <-ready: - return nil - } -} - -// TryAcquire acquires the semaphore with a weight of n without blocking. -// On success, returns true. On failure, returns false and leaves the semaphore unchanged. -func (s *Weighted) TryAcquire(n int64) bool { - s.mu.Lock() - success := s.size-s.cur >= n && s.waiters.Len() == 0 - if success { - s.cur += n - } - s.mu.Unlock() - return success -} - -// Release releases the semaphore with a weight of n. -func (s *Weighted) Release(n int64) { - s.mu.Lock() - s.cur -= n - if s.cur < 0 { - s.mu.Unlock() - panic("semaphore: released more than held") - } - s.notifyWaiters() - s.mu.Unlock() -} - -func (s *Weighted) notifyWaiters() { - for { - next := s.waiters.Front() - if next == nil { - break // No more waiters blocked. - } - - w := next.Value.(waiter) - if s.size-s.cur < w.n { - // Not enough tokens for the next waiter. We could keep going (to try to - // find a waiter with a smaller request), but under load that could cause - // starvation for large requests; instead, we leave all remaining waiters - // blocked. - // - // Consider a semaphore used as a read-write lock, with N tokens, N - // readers, and one writer. Each reader can Acquire(1) to obtain a read - // lock. The writer can Acquire(N) to obtain a write lock, excluding all - // of the readers. If we allow the readers to jump ahead in the queue, - // the writer will starve — there is always one token available for every - // reader. - break - } - - s.cur += w.n - s.waiters.Remove(next) - close(w.ready) - } -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 67f48920..2efe6e8d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -177,7 +177,7 @@ github.com/xdg-go/scram github.com/xdg-go/stringprep # github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d github.com/youmark/pkcs8 -# go.mongodb.org/mongo-driver v1.7.4 +# go.mongodb.org/mongo-driver v1.9.1 ## explicit go.mongodb.org/mongo-driver/bson go.mongodb.org/mongo-driver/bson/bsoncodec @@ -187,6 +187,7 @@ go.mongodb.org/mongo-driver/bson/bsontype go.mongodb.org/mongo-driver/bson/primitive go.mongodb.org/mongo-driver/event go.mongodb.org/mongo-driver/internal +go.mongodb.org/mongo-driver/internal/randutil go.mongodb.org/mongo-driver/mongo go.mongodb.org/mongo-driver/mongo/address go.mongodb.org/mongo-driver/mongo/description @@ -228,7 +229,6 @@ golang.org/x/crypto/sha3 golang.org/x/net/context # golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sync/errgroup -golang.org/x/sync/semaphore # golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf ## explicit golang.org/x/sys/cpu