summaryrefslogtreecommitdiff
path: root/jstests/gle/gle_after_split_failure_during_migration.js
blob: 9d0a6a9ca2a4b068f24f0a0c62ae45c498ea6bf4 (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
/**
 * SERVER-4987 This test tries to check the getLastError call will still use
 * the same connection even if a split chunk triggered while doing inserts
 * failed (cause by StaleConfigException).
 * 
 * TODO: SERVER-5175
 * This test relies on the corresponding delays inside (1) WriteBackListener::run
 * and (2) ShardStrategy::_insert and (3) receivedInsert from instance.cpp
 * to make the bug easier to manifest.
 * 
 * The purpose of (1) is to make the writebacks slower so the failed inserts won't
 * be reapplied on time.
 * 
 * The purpose of (2) is to make it easier for the moveChunk command from the other
 * mongos to interleave in between the moment the insert has set its shard version and
 * when in tries to autosplit (Note: it should be long enough to allow the moveChunk
 * to actually complete before it tries to proceed to autosplit).
 * 
 * The purpose of (3) is to make sure that the insert won't get applied to the
 * shard right away so when a different connection is used to do the getLastError,
 * the write will still not be applied.
 */
function testGleAfterSplitDuringMigration(){
    var st = new ShardingTest({ shards: 2, verbose: 2, mongos: 2,
                                other: { chunksize: 1 }});

    // Stop the balancer to prevent it from contending with the distributed lock.
    st.stopBalancer();

    var DB_NAME = jsTest.name();
    var COLL_NAME = "coll";

    var mongos = st.s0;
    var confDB = mongos.getDB( "config" );
    var coll = mongos.getCollection( DB_NAME + "." + COLL_NAME );

    var shardConn = st.d0;
    var shardColl = shardConn.getCollection( coll.getFullName() );

    var data = "x";
    var dataSize = 1024 * 256; // bytes, must be power of 2
    while( data.length < dataSize ) data += data;

    // Shard collection
    st.shardColl( coll, { _id : 1 }, false );

    var docID = 0;

    /**
     * @return {Mongo} the connection object given the name of the shard.
     */
    var getShardConn = function( shardName ) {
        var shardLoc = confDB.shards.findOne({ _id: shardName }).host;
        return new Mongo( shardLoc );
    };

    /**
     * Inserts documents using a direct shard connection to the max key chunk
     * enough to make sure that it will trigger the auto split.
     * 
     * variables from outer scope: docID, coll, confDB, data
     */
    var primeForSplitting = function() {
        var topChunk = confDB.chunks.find().sort({ max: -1 }).limit( 1 ).next();
        var shardLoc = getShardConn( topChunk.shard );
        var testColl = shardLoc.getCollection( coll.getFullName() );

        var superSaturatedChunkSize = 1024 * 1024 * 10; // 10MB
        var docsToSaturateChunkSize = superSaturatedChunkSize / dataSize;
        
        for ( var i = 0; i < docsToSaturateChunkSize; i++ ) {
            testColl.insert({ _id: docID++, val: data });
        }

        assert.eq( null, testColl.getDB().getLastError() );
    };

    /**
     * Moves a random chunk to a new shard using a different mongos.
     * 
     * @param tries {Number} number of retry attempts when the moveChunk command
     *    fails.
     * 
     * variables from outer scope: coll, st
     */
    var moveRandomChunk = function( tries ) {
        var otherConfDB = st.s1.getDB( "config" );
        var chunksCursor = otherConfDB.chunks.find().sort({ max: 1 });
        var chunkCount = chunksCursor.count();

        var randIdx = Math.floor( Math.random() * chunkCount );
        // Don't get the chunk with max/min key
        randIdx = ( randIdx == chunkCount )? randIdx - 1 : randIdx;
        randIdx = ( randIdx == 0 )? randIdx + 1 : randIdx;

        var chunk = chunksCursor.arrayAccess( randIdx );
        var chunkOwner = chunk.shard;
        var newOwner = otherConfDB.shards.findOne({ _id: { $ne: chunkOwner }})._id;

        var result = otherConfDB.adminCommand({ moveChunk: coll.getFullName(),
                                                find: { _id: chunk.min._id },
                                                to: newOwner });

        jsTest.log( "moveChunk result: " + tojson( result ));
        if( !result.ok && tries > 1 ) {
            moveRandomChunk( tries - 1 );
        }
    };

    var chunks = 0;
    do {
        coll.insert({ _id: docID++, val: data });
        chunks = mongos.getDB( "config" ).chunks.find().count();
    } while ( chunks < 5 );

    primeForSplitting();
    
    jsTest.log( "Starting the insert that should trigger auto-split." );

    // TODO: SERVER-5175 Trigger delays here
    coll.insert({ _id: docID++, val: data });
    moveRandomChunk( 3 );

    // getLastError should wait for all writes to this connection.
    var errObj = coll.getDB().getLastErrorObj();
    jsTest.log( "Last Error Object: " + tojson( errObj ));

    assert.eq( docID, coll.find().itcount(), "Count does not match!" );

    jsTest.log( "Finished counting." );

    st.stop();
}

testGleAfterSplitDuringMigration();