Skip to content

Commit c0c8966

Browse files
authored
REP-5274 Compare full index specs rather than just a subset. (#50)
Previously migration-verifier only checked the parts of index specs that go in a mongo.IndexSpecification. That struct is incomplete, though; it doesn’t, for example, include partial index filters. Thus, migration-verifier didn’t compare those parts of an index specification. This changeset alters the index-checking logic to check full index specifications. Note that this comparison is rather “coarse” and may flag some specifications as mismatching when they actually produce the same index. For example, `{foo: 1}` and `{foo: 2}` actually produce the same index, but migration-verifier will flag them as mismatches. Eventually we hope to fix that, but for now false positives are safer than incomplete checks. This does teach index verification to ignore type differences, which is a common source of false-positives in verifications. For example, `{_id: Double(1)}` vs. `{_id: Long(1)}` will no longer be flagged as a mismatch. (NB: Due to this change, the metadata DB must now be on a v6+ cluster.) This also replaces use of mongo.CollectionSpecification with a struct that allows detection of unrecognized fields. Thus we’ll avoid a similar problem for collection specifications. This also copies over mongosync’s Option implementation.
1 parent a4086df commit c0c8966

File tree

8 files changed

+976
-257
lines changed

8 files changed

+976
-257
lines changed

internal/util/collections.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package util
2+
3+
import (
4+
"context"
5+
6+
"github.com/10gen/migration-verifier/option"
7+
"github.com/pkg/errors"
8+
"go.mongodb.org/mongo-driver/bson"
9+
"go.mongodb.org/mongo-driver/bson/primitive"
10+
"go.mongodb.org/mongo-driver/mongo"
11+
)
12+
13+
// CollectionSpec is like mongo.CollectionSpecification except:
14+
// - IDIndex is a bson.Raw rather than mongo.IndexSpecification.
15+
// - It can detect unexpected fields.
16+
type CollectionSpec struct {
17+
Name string
18+
Type string
19+
Options bson.Raw
20+
Info struct {
21+
ReadOnly bool `bson:"readOnly"`
22+
UUID primitive.Binary
23+
24+
Extra map[string]any
25+
}
26+
IDIndex bson.Raw `bson:"idIndex"`
27+
28+
Extra map[string]any
29+
}
30+
31+
// FullName returns the collection's full namespace.
32+
func FullName(collection *mongo.Collection) string {
33+
return collection.Database().Name() + "." + collection.Name()
34+
}
35+
36+
// GetCollectionSpecIfExists returns the given collection’s specification,
37+
// or empty if the collection doesn’t exist. If any unexpected properties
38+
// exist in the collection specification then an error is returned.
39+
func GetCollectionSpecIfExists(
40+
ctx context.Context,
41+
coll *mongo.Collection,
42+
) (option.Option[CollectionSpec], error) {
43+
cursor, err := coll.Database().ListCollections(ctx, bson.M{"name": coll.Name()})
44+
if err != nil {
45+
return option.None[CollectionSpec](), errors.Wrapf(
46+
err,
47+
"failed to fetch %#q's specification",
48+
FullName(coll),
49+
)
50+
}
51+
52+
var specs []CollectionSpec
53+
err = cursor.All(ctx, &specs)
54+
if err != nil {
55+
return option.None[CollectionSpec](), errors.Wrapf(
56+
err,
57+
"failed to parse %#q's specification",
58+
FullName(coll),
59+
)
60+
}
61+
62+
switch len(specs) {
63+
case 0:
64+
return option.None[CollectionSpec](), nil
65+
case 1:
66+
if len(specs[0].Extra) > 0 || len(specs[0].Info.Extra) > 0 {
67+
return option.None[CollectionSpec](), errors.Wrapf(
68+
err,
69+
"%#q's specification (%v) contains unrecognized fields",
70+
FullName(coll),
71+
specs[0],
72+
)
73+
}
74+
75+
return option.Some(specs[0]), nil
76+
}
77+
78+
return option.None[CollectionSpec](), errors.Wrapf(
79+
err,
80+
"received multiple results (%v) when fetching %#q's specification",
81+
specs,
82+
FullName(coll),
83+
)
84+
}

0 commit comments

Comments
 (0)