summaryrefslogtreecommitdiff
path: root/jstests/sharding/shard_collection_existing_zones.js
blob: d6cb10c304eda4a63ded1e9765e8b58140f5a67f (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
// Test that shardCollection uses existing zone info to validate
// shard keys and do initial chunk splits.
(function() {
    'use strict';

    var st = new ShardingTest({mongos: 1, shards: 3});
    var kDbName = 'test';
    var kCollName = 'foo';
    var ns = kDbName + '.' + kCollName;
    var zoneName = 'zoneName';
    var mongos = st.s0;
    var testDB = mongos.getDB(kDbName);
    var configDB = mongos.getDB('config');
    var shardName = st.shard0.shardName;
    assert.commandWorked(mongos.adminCommand({enableSharding: kDbName}));

    /**
     * Test that shardCollection correctly validates that a zone is associated with a shard.
     */
    function testShardZoneAssociationValidation(proposedShardKey, numberLongMin, numberLongMax) {
        var zoneMin = numberLongMin ? {x: NumberLong(0)} : {x: 0};
        var zoneMax = numberLongMax ? {x: NumberLong(10)} : {x: 10};
        assert.commandWorked(configDB.tags.insert(
            {_id: {ns: ns, min: zoneMin}, ns: ns, min: zoneMin, max: zoneMax, tag: zoneName}));

        var tagDoc = configDB.tags.findOne();
        assert.eq(ns, tagDoc.ns);
        assert.eq(zoneMin, tagDoc.min);
        assert.eq(zoneMax, tagDoc.max);
        assert.eq(zoneName, tagDoc.tag);

        assert.commandFailed(mongos.adminCommand({shardCollection: ns, key: proposedShardKey}));

        assert.commandWorked(st.s.adminCommand({addShardToZone: shardName, zone: zoneName}));
        assert.commandWorked(mongos.adminCommand({shardCollection: ns, key: proposedShardKey}));

        assert.commandWorked(testDB.runCommand({drop: kCollName}));
    }

    /**
     * Test that shardCollection correctly validates shard key against existing zones.
     */
    function testShardKeyValidation(proposedShardKey, numberLongMin, numberLongMax, success) {
        assert.commandWorked(testDB.foo.createIndex(proposedShardKey));
        assert.commandWorked(st.s.adminCommand({addShardToZone: shardName, zone: zoneName}));

        var zoneMin = numberLongMin ? {x: NumberLong(0)} : {x: 0};
        var zoneMax = numberLongMax ? {x: NumberLong(10)} : {x: 10};
        assert.commandWorked(st.s.adminCommand(
            {updateZoneKeyRange: ns, min: zoneMin, max: zoneMax, zone: zoneName}));

        var tagDoc = configDB.tags.findOne();
        jsTestLog("xxx tag doc " + tojson(tagDoc));
        assert.eq(ns, tagDoc.ns);
        assert.eq(zoneMin, tagDoc.min);
        assert.eq(zoneMax, tagDoc.max);
        assert.eq(zoneName, tagDoc.tag);

        if (success) {
            assert.commandWorked(mongos.adminCommand({shardCollection: ns, key: proposedShardKey}));
        } else {
            assert.commandFailed(mongos.adminCommand({shardCollection: ns, key: proposedShardKey}));
        }

        assert.commandWorked(testDB.runCommand({drop: kCollName}));
    }

    /**
     * Test that shardCollection uses existing zone ranges to split chunks.
     */
    function testChunkSplits(collectionExists) {
        var shardKey = {x: 1};
        var ranges = [
            {min: {x: 0}, max: {x: 10}},
            {min: {x: 10}, max: {x: 20}},
            {min: {x: 30}, max: {x: 40}}
        ];
        var shards = configDB.shards.find().toArray();
        assert.eq(ranges.length, shards.length);
        if (collectionExists) {
            assert.commandWorked(testDB.foo.createIndex(shardKey));
        }

        // create zones:
        // shard0 - zonename0 - [0, 10)
        // shard1 - zonename0 - [10, 20)
        // shard2 - zonename0 - [30, 40)
        for (var i = 0; i < shards.length; i++) {
            assert.commandWorked(
                st.s.adminCommand({addShardToZone: shards[i]._id, zone: zoneName + i}));
            assert.commandWorked(st.s.adminCommand({
                updateZoneKeyRange: ns,
                min: ranges[i].min,
                max: ranges[i].max,
                zone: zoneName + i
            }));
        }
        assert.eq(configDB.tags.find().count(),
                  shards.length,
                  "failed to create tag documents correctly");
        assert.eq(
            configDB.chunks.find({ns: ns}).count(),
            0,
            "expect to see no chunk documents for the collection before shardCollection is run");

        // shard the collection and validate the resulting chunks
        assert.commandWorked(mongos.adminCommand({shardCollection: ns, key: shardKey}));
        var expectedChunks = [
            {range: [{x: {"$minKey": 1}}, {x: 0}], shardId: st.shard0.shardName},
            {range: [{x: 0}, {x: 10}], shardId: st.shard0.shardName},  // pre-defined
            {range: [{x: 10}, {x: 20}], shardId: st.shard1.shardName},
            {range: [{x: 20}, {x: 30}], shardId: st.shard1.shardName},  // pre-defined
            {range: [{x: 30}, {x: 40}], shardId: st.shard2.shardName},  // pre-defined
            {range: [{x: 40}, {x: {"$maxKey": 1}}], shardId: st.shard2.shardName}
        ];
        var chunkDocs = configDB.chunks.find({ns: ns}).toArray();
        assert.eq(chunkDocs.length,
                  expectedChunks.length,
                  "shardCollection failed to create chunk documents correctly");
        for (var i = 0; i < chunkDocs.length; i++) {
            var errMsg = "expect to see chunk " + tojson(expectedChunks[i]) + " but found chunk " +
                tojson(chunkDocs[i]);
            assert.eq(expectedChunks[i].range[0], chunkDocs[i].min, errMsg);
            assert.eq(expectedChunks[i].range[1], chunkDocs[i].max, errMsg);
            assert.eq(expectedChunks[i].shardId, chunkDocs[i].shard, errMsg);
        }

        assert.commandWorked(testDB.runCommand({drop: kCollName}));
    }

    // test that shardCollection checks that a zone is associated with a shard.
    testShardZoneAssociationValidation({x: 1}, false, false);

    // test that shardCollection uses existing zones to validate shard key
    testShardKeyValidation({x: 1}, false, false, true);

    // cannot use a completely different key from the zone shard key or a key
    // that has the zone shard key as a prefix is not allowed.
    testShardKeyValidation({y: 1}, false, false, false);
    testShardKeyValidation({x: 1, y: 1}, false, false, false);

    // can only do hash sharding when the boundaries are of type NumberLong.
    testShardKeyValidation({x: "hashed"}, false, false, false);
    testShardKeyValidation({x: "hashed"}, true, false, false);
    testShardKeyValidation({x: "hashed"}, false, true, false);
    testShardKeyValidation({x: "hashed"}, true, true, true);

    assert.commandWorked(st.s.adminCommand({removeShardFromZone: shardName, zone: zoneName}));

    // test that shardCollection uses zone ranges to split chunks

    testChunkSplits(false);
    testChunkSplits(true);

    st.stop();
})();