summaryrefslogtreecommitdiff
path: root/jstests/sharding/move_primary_basic.js
blob: f2612add02135c9d8047ef1ca8552059323acb68 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
(function() {
'use strict';

load('jstests/libs/feature_flag_util.js');

function collectionExists(shard, dbName, collName) {
    return Array.contains(shard.getDB(dbName).getCollectionNames(), collName);
}

var st = new ShardingTest({mongos: 1, shards: 2});

var mongos = st.s0;
var shard0 = st.shard0;
var shard1 = st.shard1;
var config = st.config;

const dbName = 'test_db';
const coll1Name = 'test_coll_1';
const coll2Name = 'test_coll_2';
const coll1NS = dbName + '.' + coll1Name;
const coll2NS = dbName + '.' + coll2Name;

assert.commandWorked(mongos.adminCommand({enableSharding: dbName, primaryShard: shard0.shardName}));
assert.commandWorked(mongos.getCollection(coll1NS).insert({name: 'Tom'}));
assert.commandWorked(mongos.getCollection(coll1NS).insert({name: 'Dick'}));
assert.commandWorked(mongos.getCollection(coll2NS).insert({name: 'Harry'}));

assert.commandWorked(st.s.adminCommand({shardCollection: coll2NS, key: {_id: 1}}));

jsTest.log('Test preconditions');
{
    // Fail with internal databases.
    assert.commandFailed(mongos.adminCommand({movePrimary: 'config', to: shard1.shardName}));
    assert.commandFailed(mongos.adminCommand({movePrimary: 'admin', to: shard1.shardName}));
    assert.commandFailed(mongos.adminCommand({movePrimary: 'local', to: shard1.shardName}));

    // Fail with invalid database names.
    assert.commandFailed(mongos.adminCommand({movePrimary: '', to: shard1.shardName}));
    assert.commandFailed(mongos.adminCommand({movePrimary: 'a.b', to: shard1.shardName}));

    // Fail against a non-admin database.
    assert.commandFailedWithCode(
        mongos.getDB('test').runCommand({movePrimary: dbName, to: shard1.shardName}),
        ErrorCodes.Unauthorized);

    // Fail if the destination shard is invalid or does not exist.
    assert.commandFailed(mongos.adminCommand({movePrimary: dbName}));
    assert.commandFailed(mongos.adminCommand({movePrimary: dbName, to: ''}));
    assert.commandFailed(mongos.adminCommand({movePrimary: dbName, to: 'Unknown'}));

    // Succeed if the destination shard is already the primary for the given database.
    assert.commandWorked(mongos.adminCommand({movePrimary: dbName, to: shard0.shardName}));
}

jsTest.log('Test that only unsharded collections are moved');
{
    {
        // Expected documents placement before moving primary to shard1:
        //   * shard0: 3 docs
        //     1: { name : 'Tom'   }
        //     2: { name : 'Dick'  }
        //     3: { name : 'Harry' }
        //   * shard1: 0 docs

        // The sharded collection's documents are on shard0.
        assert.eq(2, shard0.getCollection(coll1NS).find().itcount());
        assert.eq(0, shard1.getCollection(coll1NS).find().itcount());

        // The unsharded collection's documents are on shard0.
        assert.eq(1, shard0.getCollection(coll2NS).find().itcount());
        assert.eq(0, shard1.getCollection(coll2NS).find().itcount());
    }

    assert.commandWorked(mongos.adminCommand({movePrimary: dbName, to: shard1.shardName}));

    {
        // Expected documents placement after moving primary to shard1:
        //   * shard0: 1 doc
        //     1: { name : 'Harry' }
        //   * shard1: 2 docs
        //     1: { name : 'Tom'   }
        //     2: { name : 'Dick'  }

        // The sharded collection's documents are now on shard1.
        assert.eq(0, shard0.getCollection(coll1NS).find().itcount());
        assert.eq(2, shard1.getCollection(coll1NS).find().itcount());

        // The unsharded collection's documents are still on shard0.
        assert.eq(1, shard0.getCollection(coll2NS).find().itcount());
        assert.eq(0, shard1.getCollection(coll2NS).find().itcount());
    }

    assert.commandWorked(mongos.adminCommand({movePrimary: dbName, to: shard0.shardName}));

    {
        // Expected documents placement after moving primary back to shard0:
        //   * shard0: 3 docs
        //     1: { name : 'Tom'   }
        //     2: { name : 'Dick'  }
        //     3: { name : 'Harry' }
        //   * shard1: 0 docs

        // The sharded collection's documents are on shard0.
        assert.eq(2, shard0.getCollection(coll1NS).find().itcount());
        assert.eq(0, shard1.getCollection(coll1NS).find().itcount());

        // The unsharded collection's documents are on shard0.
        assert.eq(1, shard0.getCollection(coll2NS).find().itcount());
        assert.eq(0, shard1.getCollection(coll2NS).find().itcount());
    }
}

jsTest.log('Test that orphaned documents on recipient causes the operation to fail');
{
    // Insert an orphaned document on shard1.
    assert.commandWorked(shard1.getCollection(coll1NS).insertOne({name: 'Emma'}));

    // The documents are on both the shards.
    assert.eq(2, shard0.getCollection(coll1NS).find().itcount());
    assert.eq(1, shard1.getCollection(coll1NS).find().itcount());

    assert.commandFailedWithCode(mongos.adminCommand({movePrimary: dbName, to: shard1.shardName}),
                                 ErrorCodes.NamespaceExists);

    const expectDropOnFailure =
        FeatureFlagUtil.isPresentAndEnabled(config.admin, 'OnlineMovePrimaryLifecycle');

    if (expectDropOnFailure) {
        // The orphaned collection on shard1 should have been dropped due to the previous failure.
        assert.eq(2, shard0.getCollection(coll1NS).find().itcount());
        assert(!collectionExists(shard1, dbName, coll1Name));

        // Create another empty collection.
        shard1.getDB(dbName).createCollection(coll1Name);
    } else {
        // The documents are on both the shards.
        assert.eq(2, shard0.getCollection(coll1NS).find().itcount());
        assert.eq(1, shard1.getCollection(coll1NS).find().itcount());

        // Remove the orphaned document on shard1 leaving an empty collection.
        assert.commandWorked(shard1.getCollection(coll1NS).remove({name: 'Emma'}));
        assert.eq(0, shard1.getCollection(coll1NS).find().itcount());
    }

    assert.commandFailedWithCode(mongos.adminCommand({movePrimary: dbName, to: shard1.shardName}),
                                 ErrorCodes.NamespaceExists);

    if (expectDropOnFailure) {
        // The orphaned collection on shard1 should have been dropped due to the previous failure.
        assert(!collectionExists(shard1, dbName, coll1Name));
    } else {
        // Drop the orphaned collection on shard1.
        shard1.getCollection(coll1NS).drop();
    }
}

jsTest.log('Test that metadata has changed');
{
    //  The current primary shard is shard1.
    const previousMetadata = config.databases.findOne({_id: dbName});
    assert.eq(shard0.shardName, previousMetadata.primary);

    assert.commandWorked(mongos.adminCommand({movePrimary: dbName, to: shard1.shardName}));

    // The new primary shard is shard0.
    const nextMetadata = config.databases.findOne({_id: dbName});
    assert.eq(shard1.shardName, nextMetadata.primary);

    // The identifiers have not changed, but the version (lastMod) has been bumped.
    assert.eq(previousMetadata.version.uuid, nextMetadata.version.uuid);
    assert.eq(previousMetadata.version.timestamp, nextMetadata.version.timestamp);
    assert.eq(previousMetadata.version.lastMod + 1, nextMetadata.version.lastMod);
}

st.stop();
})();