Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
141 commits
Select commit Hold shift + click to select a range
687a57e
move to common
FGasper Nov 11, 2025
61b89e3
move
FGasper Nov 11, 2025
3d35d29
fix lint
FGasper Nov 11, 2025
1c672f9
unused
FGasper Nov 11, 2025
aefe4d6
pointer receivers
FGasper Nov 11, 2025
1cb493b
renames/refacotrs
FGasper Nov 11, 2025
08d5633
cleanup
FGasper Nov 11, 2025
c9e4610
tidy
FGasper Nov 11, 2025
c7d9edd
string
FGasper Nov 11, 2025
196f887
more renames
FGasper Nov 11, 2025
06f7794
move
FGasper Nov 11, 2025
0b13627
metadata
FGasper Nov 11, 2025
25b7e61
comment
FGasper Nov 11, 2025
3284468
comments
FGasper Nov 11, 2025
15a7636
remove persistor error
FGasper Nov 12, 2025
df29cde
remove doneChan
FGasper Nov 12, 2025
fc6c8ce
remove reader error channel
FGasper Nov 12, 2025
d47c40a
allow eventual to accept nil -- MUST TEST!
FGasper Nov 12, 2025
4e38f04
allow premature exit
FGasper Nov 12, 2025
0d4e406
rename & move
FGasper Nov 12, 2025
95129ef
Merge branch 'main' into REP-6804-scratch
FGasper Nov 12, 2025
4e82706
Merge branch 'REP-6804-scratch' into REP-6804-fix-context
FGasper Nov 12, 2025
e23a503
move
FGasper Nov 12, 2025
d61a8f1
handling
FGasper Nov 12, 2025
b05ffe3
add oplog reader & alia
FGasper Nov 12, 2025
c6025f5
dedupe retry of change reader
FGasper Nov 12, 2025
8751b11
format
FGasper Nov 12, 2025
d3070e2
oops
FGasper Nov 12, 2025
06bc31b
nolint
FGasper Nov 12, 2025
1cbb232
fix test
FGasper Nov 12, 2025
51f4163
ddl allowance
FGasper Nov 12, 2025
6918fda
token
FGasper Nov 12, 2025
eb0464d
test 2nd time
FGasper Nov 12, 2025
7b30bff
save
FGasper Nov 12, 2025
4df242e
add comment
FGasper Nov 12, 2025
d2b9e18
Merge branch 'REP-6804-fix-context' into REP-6804-mongod-oplog-final
FGasper Nov 12, 2025
e5a1596
tolerate later startAtTs
FGasper Nov 12, 2025
90f9123
collmod test
FGasper Nov 12, 2025
11140ae
empty ts
FGasper Nov 12, 2025
8f6871b
read oplog for 4.2
FGasper Nov 12, 2025
3c6380c
Merge branch 'main' into REP-6804-fix-context
FGasper Nov 12, 2025
5337613
Merge branch 'REP-6804-fix-context' into REP-6804-mongod-oplog-final
FGasper Nov 12, 2025
0fbc28e
maybe fix test?
FGasper Nov 12, 2025
6052ecb
avoid $switch for 4.2.
FGasper Nov 13, 2025
491e8f8
4.4
FGasper Nov 13, 2025
6c0aa33
compat
FGasper Nov 13, 2025
8c0a1d3
switch is OK
FGasper Nov 13, 2025
1bfbe36
no oplog for 4.2 for now
FGasper Nov 13, 2025
e80cba3
projection is only 4.4 anyway
FGasper Nov 13, 2025
f3bf387
allow non-expr oplog
FGasper Nov 13, 2025
f07b8de
handle applyOps
FGasper Nov 13, 2025
7840134
clone
FGasper Nov 13, 2025
ed11123
Merge branch 'main' into REP-6804-mongod-oplog-final
FGasper Nov 13, 2025
acbbbbe
options
FGasper Nov 13, 2025
ddeb8f3
github ci
FGasper Nov 13, 2025
e32835b
err check
FGasper Nov 13, 2025
bb78043
array
FGasper Nov 13, 2025
bf719a6
quotes
FGasper Nov 13, 2025
a74853e
quotes again
FGasper Nov 13, 2025
1f1fdb4
support ns filter in oplog
FGasper Nov 13, 2025
0622651
show Max the wonky
FGasper Nov 13, 2025
0b68637
wth
FGasper Nov 14, 2025
00d5a24
still failing :(
FGasper Nov 14, 2025
3d20e9c
neutralize session
FGasper Nov 14, 2025
2a5e55a
fixed test
FGasper Nov 14, 2025
eeb1f4f
more log
FGasper Nov 14, 2025
1296de2
Merge branch 'main' into REP-6804-mongod-oplog-final
FGasper Nov 14, 2025
b5ccf30
lint
FGasper Nov 14, 2025
77ea0d7
remove prints
FGasper Nov 14, 2025
43a588f
no snapshot
FGasper Nov 14, 2025
36ddf24
majority
FGasper Nov 14, 2025
aa86c4f
kill txns
FGasper Nov 14, 2025
9677fdd
local rc
FGasper Nov 14, 2025
683b055
redundant
FGasper Nov 14, 2025
b0f2aad
invert order
FGasper Nov 14, 2025
bc3ab8c
wait for catchup
FGasper Nov 14, 2025
11be48d
clear out
FGasper Nov 14, 2025
8c7578f
fix??
FGasper Nov 14, 2025
1784a7b
tweak
FGasper Nov 14, 2025
3160dc6
switch over
FGasper Nov 14, 2025
263c9e7
fall back to start ts as needed
FGasper Nov 14, 2025
95e7566
Merge branch 'REP-6832-afterclustertime-latest-clustertime' into REP-…
FGasper Nov 14, 2025
d009954
agg polish
FGasper Nov 14, 2025
f10c907
fix Eq
FGasper Nov 14, 2025
f05a4c6
use driver API
FGasper Nov 14, 2025
93a808d
Merge branch 'REP-6832-afterclustertime-latest-clustertime' into REP-…
FGasper Nov 14, 2025
cf08833
sc
FGasper Nov 14, 2025
22d5775
revert
FGasper Nov 14, 2025
894f454
Merge branch 'REP-6832-afterclustertime-latest-clustertime' into REP-…
FGasper Nov 14, 2025
f2ca1e6
compulsory timestamp
FGasper Nov 15, 2025
22e5072
start handling
FGasper Nov 15, 2025
88b0657
another
FGasper Nov 15, 2025
ae85756
fix another
FGasper Nov 15, 2025
37373ac
fix
FGasper Nov 15, 2025
b30e9a8
fix again
FGasper Nov 15, 2025
0546f15
fix test
FGasper Nov 15, 2025
1ffb5f6
more
FGasper Nov 15, 2025
a463151
move
FGasper Nov 15, 2025
a5ab503
save
FGasper Nov 15, 2025
fe22242
ctx
FGasper Nov 15, 2025
474ae71
comment
FGasper Nov 16, 2025
c9c07f4
Merge branch 'REP-6832-afterclustertime-latest-clustertime' into REP-…
FGasper Nov 18, 2025
d038582
try 3.6 here
FGasper Nov 18, 2025
ab63521
Revert "try 3.6 here"
FGasper Nov 18, 2025
1a70ae7
add
FGasper Nov 18, 2025
b2390d8
Revert "add"
FGasper Nov 18, 2025
4631895
add majority RC
FGasper Nov 18, 2025
40044f5
dummy
FGasper Nov 18, 2025
e9f534f
dummy
FGasper Nov 19, 2025
b3b1283
Merge branch 'main' into REP-6832-afterclustertime-latest-clustertime
FGasper Nov 19, 2025
7f658a9
Merge branch 'REP-6832-afterclustertime-latest-clustertime' into REP-…
FGasper Nov 19, 2025
22ac4e6
Merge branch 'main' into REP-6804-mongod-oplog-final
FGasper Nov 19, 2025
858b7ff
add doc
FGasper Nov 19, 2025
3211a98
no pointer
FGasper Nov 19, 2025
0dfe832
revert some
FGasper Nov 19, 2025
6fb0682
tweak
FGasper Nov 19, 2025
1dc0576
comments
FGasper Nov 19, 2025
627e793
fix
FGasper Nov 19, 2025
d001fde
revert
FGasper Nov 19, 2025
bf55759
don’t warn
FGasper Nov 19, 2025
a81561c
fix
FGasper Nov 20, 2025
43e5a88
Printf
FGasper Nov 21, 2025
9fe9613
comments
FGasper Nov 21, 2025
88f7d22
remove TODO
FGasper Nov 21, 2025
b6692bc
panic clearer
FGasper Nov 21, 2025
b1c5390
document limitation
FGasper Nov 21, 2025
a7294d0
Forbid changing change reader opts.
FGasper Nov 24, 2025
93b67d8
confirm failure
FGasper Nov 24, 2025
dbb15a0
moar doc
FGasper Nov 24, 2025
c2f422c
ofix test
FGasper Nov 24, 2025
7e917b3
avoid sharded w/ oplog
FGasper Nov 24, 2025
e6e59f6
Merge branch 'main' into REP-6804-mongod-oplog-final
FGasper Nov 27, 2025
fc1d80f
rename
FGasper Nov 27, 2025
6da0994
Withhold pending review - fixes
FGasper Dec 3, 2025
58f3846
renames & such from Jian’s review
FGasper Dec 3, 2025
0d284a3
fix & add more tests
FGasper Dec 3, 2025
542ebdf
add unit test
FGasper Dec 3, 2025
32445e9
tweak docs
FGasper Dec 3, 2025
4cfdbd6
fix oplog tests
FGasper Dec 3, 2025
6b33e8c
slip test
FGasper Dec 3, 2025
9b75105
replace w/ 4.2
FGasper Dec 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion .github/workflows/all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,41 @@ jobs:
- mongodb_versions: [ '4.2.5', '6.0' ]
topology: replset

- mongodb_versions: [ '4.0', '8.0' ]
topology: replset
srcChangeReader: tailOplog
dstChangeReader: tailOplog

- mongodb_versions: [ '4.2', '8.0' ]
topology: replset
srcChangeReader: tailOplog
dstChangeReader: tailOplog

- mongodb_versions: [ '4.4', '8.0' ]
topology: replset
srcChangeReader: tailOplog
dstChangeReader: tailOplog

- mongodb_versions: [ '5.0', '8.0' ]
topology: replset
srcChangeReader: tailOplog
dstChangeReader: tailOplog

- mongodb_versions: [ '6.0', '8.0' ]
topology: replset
srcChangeReader: tailOplog
dstChangeReader: tailOplog

- mongodb_versions: [ '7.0', '8.0' ]
topology: replset
srcChangeReader: tailOplog
dstChangeReader: tailOplog

- mongodb_versions: [ '8.0', '8.0' ]
topology: replset
srcChangeReader: tailOplog
dstChangeReader: tailOplog

exclude:
- mongodb_versions: [ '4.2', '4.2' ]
toHashedIndexKey: true
Expand Down Expand Up @@ -69,6 +104,9 @@ jobs:

toHashedIndexKey: [true, false]

srcChangeReader: [changeStream]
dstChangeReader: [changeStream]

topology:
- replset
- replset-to-sharded
Expand All @@ -78,7 +116,7 @@ jobs:
# versions need.
runs-on: ubuntu-22.04

name: ${{ matrix.mongodb_versions[0] }} to ${{ matrix.mongodb_versions[1] }}, ${{ matrix.topology }}${{ matrix.toHashedIndexKey && ', hashed doc compare' || '' }}
name: ${{ matrix.mongodb_versions[0] }} to ${{ matrix.mongodb_versions[1] }}, ${{ matrix.topology }}${{ matrix.toHashedIndexKey && ', hashed doc compare' || '' }}, srcChangeReader=${{ matrix.srcChangeReader }}, dstChangeReader=${{ matrix.dstChangeReader }}

steps:
- run: uname -a
Expand Down Expand Up @@ -124,6 +162,9 @@ jobs:
env:
MVTEST_DOC_COMPARE_METHOD: ${{matrix.toHashedIndexKey && 'toHashedIndexKey' || ''}}

MVTEST_SRC_CHANGE_READER: ${{matrix.srcChangeReader}}
MVTEST_DST_CHANGE_READER: ${{matrix.dstChangeReader}}

MVTEST_SRC: ${{ (matrix.topology == 'sharded') && env.shardedSrcConnStr || env.replsetSrcConnStr }}
MVTEST_DST: ${{ (matrix.topology == 'sharded' || matrix.topology == 'replset-to-sharded') && env.shardedDstConnStr || env.replsetDstConnStr }}

Expand Down
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ The verifier will now check to completion to make sure that there are no inconsi
| `--dstNamespace <namespaces>` | destination namespaces to check |
| `--metaDBName <name>` | name of the database in which to store verification metadata (default: "migration_verification_metadata") |
| `--docCompareMethod` | How to compare documents. See below for details. |
| `--srcChangeReader` | How to read changes from the source. See below for details. |
| `--dstChangeReader` | How to read changes from the destination. See below for details. |
| `--start` | Start checking documents right away rather than waiting for a `/check` API request. |
| `--verifyAll` | If set, verify all user namespaces |
| `--clean` | If set, drop all previous verification metadata before starting |
Expand Down Expand Up @@ -198,8 +200,6 @@ connection strings in the following environment variables:

The migration-verifier has two steps:



1. The initial check
1. The verifier partitions up the data into 400MB (configurable) chunks and spins up many worker goroutines (threads) to read from both the source and destination.
2. The verifier compares the documents on the source and destination by bytes and if they are different, it then checks field by field in case the field ordering has changed (since field ordering isn't required to be the same for the migration to be a success)
Expand Down Expand Up @@ -392,6 +392,19 @@ Full-document verification methods allow migration-verifier to diagnose mismatch

Additionally, because the amount of data sent to migration-verifier doesn’t actually reflect the documents’ size, no meaningful statistics are shown concerning the collection data size. Document counts, of course, are still shown.

# Change reading methods

NB: If the verifier restarts, it **MUST** use the same change reader options
as before, or it will fail immediately.

## `changeStream`

The default. The verifier will read a change stream, which works seamlessly on sharded or unsharded clusters.

## `tailOplog`

The verifier will read the oplog continually instead of reading a change stream. This is generally faster, but it doesn’t work in sharded clusters.

# Known Issues

- The verifier may report missing documents on the destination that don’t actually appear to be missing (i.e., a nonexistent problem). This has been hard to reproduce. If missing documents are reported, it is good practice to check for false positives.
Expand Down
208 changes: 208 additions & 0 deletions agg/agg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
// Package agg provides convenience types for aggregation operators.
// This yields two major advantages over using bson.D or bson.M:
// - simpler syntax
// - auto-completion (i.e., via gopls)
//
// Guiding principles are:
// - Prefer [1]any for unary operators (e.g., `$bsonSize`).
// - Prefer struct types for operators with named parameters.
// - Use functions sparingly, e.g., for “tuple” operators like `$in`.
package agg

import (
"go.mongodb.org/mongo-driver/v2/bson"
)

type Eq []any

var _ bson.Marshaler = Eq{}

func (e Eq) MarshalBSON() ([]byte, error) {
return bson.Marshal(bson.D{{"$eq", []any(e)}})
}

// ---------------------------------------------

func In[T any](needle any, haystack []T) bson.D {
return bson.D{{"$in", bson.A{needle, haystack}}}
}

// ---------------------------------------------

type BSONSize [1]any

var _ bson.Marshaler = BSONSize{}

func (b BSONSize) MarshalBSON() ([]byte, error) {
return bson.Marshal(bson.D{{"$bsonSize", b[0]}})
}

// ---------------------------------------------

type Type [1]any

var _ bson.Marshaler = Type{}

func (t Type) MarshalBSON() ([]byte, error) {
return bson.Marshal(bson.D{{"$type", t[0]}})
}

// ---------------------------------------------

type Not [1]any

var _ bson.Marshaler = Type{}

func (n Not) MarshalBSON() ([]byte, error) {
return bson.Marshal(bson.D{{"$not", n[0]}})
}

// ---------------------------------------------

type And []any

var _ bson.Marshaler = And{}

func (a And) MarshalBSON() ([]byte, error) {
return bson.Marshal(bson.D{
{"$and", []any(a)},
})
}

// ---------------------------------------------

type Or []any

var _ bson.Marshaler = Or{}

func (o Or) MarshalBSON() ([]byte, error) {
return bson.Marshal(bson.D{
{"$or", []any(o)},
})
}

// ---------------------------------------------

type MergeObjects []any

var _ bson.Marshaler = MergeObjects{}

func (m MergeObjects) MarshalBSON() ([]byte, error) {
return bson.Marshal(bson.D{
{"$mergeObjects", []any(m)},
})
}

// ---------------------------------------------

type Cond struct {
If, Then, Else any
}

var _ bson.Marshaler = Cond{}

func (c Cond) D() bson.D {
return bson.D{
{"$cond", bson.D{
{"if", c.If},
{"then", c.Then},
{"else", c.Else},
}},
}
}

func (c Cond) MarshalBSON() ([]byte, error) {
return bson.Marshal(c.D())
}

// ---------------------------------------------

type Switch struct {
Branches []SwitchCase
Default any
}

var _ bson.Marshaler = Switch{}

type SwitchCase struct {
Case any
Then any
}

func (s Switch) D() bson.D {
return bson.D{{"$switch", bson.D{
{"branches", s.Branches},
{"default", s.Default},
}}}
}

func (s Switch) MarshalBSON() ([]byte, error) {
return bson.Marshal(s.D())
}

// ---------------------------------------------

type ArrayElemAt struct {
Array any
Index int
}

var _ bson.Marshaler = ArrayElemAt{}

func (a ArrayElemAt) D() bson.D {
return bson.D{{"$arrayElemAt", bson.A{
a.Array,
a.Index,
}}}
}

func (a ArrayElemAt) MarshalBSON() ([]byte, error) {
return bson.Marshal(a.D())
}

// ---------------------------------------------

type Map struct {
Input, As, In any
}

var _ bson.Marshaler = Map{}

func (m Map) D() bson.D {
return bson.D{
{"$map", bson.D{
{"input", m.Input},
{"as", m.As},
{"in", m.In},
}},
}
}

func (m Map) MarshalBSON() ([]byte, error) {
return bson.Marshal(m.D())
}

// ------------------------------------------

type Filter struct {
Input, As, Cond, Limit any
}

var _ bson.Marshaler = Filter{}

func (f Filter) D() bson.D {
d := bson.D{
{"input", f.Input},
{"as", f.As},
{"cond", f.Cond},
}

if f.Limit != nil {
d = append(d, bson.E{"limit", f.Limit})
}
return bson.D{{"$filter", d}}
}

func (f Filter) MarshalBSON() ([]byte, error) {
return bson.Marshal(f.D())
}
12 changes: 12 additions & 0 deletions agg/helpers/exist.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package helpers

import (
"github.com/10gen/migration-verifier/agg"
"go.mongodb.org/mongo-driver/v2/bson"
)

type Exists [1]any

func (e Exists) MarshalBSON() ([]byte, error) {
return bson.Marshal(agg.Not{agg.Eq{"missing", agg.Type{e[0]}}})
}
27 changes: 27 additions & 0 deletions agg/helpers/string.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Package helpers exposes functions that express common operations
// that don’t map to a single aggregation operator.
package helpers

import (
"go.mongodb.org/mongo-driver/v2/bson"
)

// StringHasPrefix parallels Go’s strings.HasPrefix.
type StringHasPrefix struct {
FieldRef any
Prefix string
}

func (sp StringHasPrefix) MarshalBSON() ([]byte, error) {
return bson.Marshal(bson.D{
{"$eq", bson.A{
0,
bson.D{{"$indexOfCP", bson.A{
sp.FieldRef,
sp.Prefix,
0,
1,
}}},
}},
})
}
Loading