~evarlast/ubuntu/utopic/mongodb/upstart-workaround-debian-bug-718702

« back to all changes in this revision

Viewing changes to src/mongo/dbtests/indexupdatetests.cpp

  • Committer: Package Import Robot
  • Author(s): James Page, James Page, Robie Basak
  • Date: 2013-05-29 17:44:42 UTC
  • mfrom: (44.1.7 sid)
  • Revision ID: package-import@ubuntu.com-20130529174442-z0a4qmoww4y0t458
Tags: 1:2.4.3-1ubuntu1
[ James Page ]
* Merge from Debian unstable, remaining changes:
  - Enable SSL support:
    + d/control: Add libssl-dev to BD's.
    + d/rules: Enabled --ssl option.
    + d/mongodb.conf: Add example SSL configuration options.
  - d/mongodb-server.mongodb.upstart: Add upstart configuration.
  - d/rules: Don't strip binaries during scons build for Ubuntu.
  - d/control: Add armhf to target archs.
  - d/p/SConscript.client.patch: fixup install of client libraries.
  - d/p/0010-install-libs-to-usr-lib-not-usr-lib64-Closes-588557.patch:
    Install libraries to lib not lib64.
* Dropped changes:
  - d/p/arm-support.patch: Included in Debian.
  - d/p/double-alignment.patch: Included in Debian.
  - d/rules,control: Debian also builds with avaliable system libraries
    now.
* Fix FTBFS due to gcc and boost upgrades in saucy:
  - d/p/0008-ignore-unused-local-typedefs.patch: Add -Wno-unused-typedefs
    to unbreak building with g++-4.8.
  - d/p/0009-boost-1.53.patch: Fixup signed/unsigned casting issue.

[ Robie Basak ]
* d/p/0011-Use-a-signed-char-to-store-BSONType-enumerations.patch: Fixup
  build failure on ARM due to missing signed'ness of char cast.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//@file indexupdatetests.cpp : mongo/db/index_update.{h,cpp} tests
 
2
 
 
3
/**
 
4
 *    Copyright (C) 2012 10gen Inc.
 
5
 *
 
6
 *    This program is free software: you can redistribute it and/or  modify
 
7
 *    it under the terms of the GNU Affero General Public License, version 3,
 
8
 *    as published by the Free Software Foundation.
 
9
 *
 
10
 *    This program is distributed in the hope that it will be useful,
 
11
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 *    GNU Affero General Public License for more details.
 
14
 *
 
15
 *    You should have received a copy of the GNU Affero General Public License
 
16
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
 */
 
18
 
 
19
#include "mongo/db/index_update.h"
 
20
 
 
21
#include "mongo/db/btree.h"
 
22
#include "mongo/db/btreecursor.h"
 
23
#include "mongo/db/dbhelpers.h"
 
24
#include "mongo/db/kill_current_op.h"
 
25
#include "mongo/db/pdfile.h"
 
26
#include "mongo/db/sort_phase_one.h"
 
27
#include "mongo/platform/cstdint.h"
 
28
 
 
29
#include "mongo/dbtests/dbtests.h"
 
30
 
 
31
namespace IndexUpdateTests {
 
32
 
 
33
    static const char* const _ns = "unittests.indexupdate";
 
34
    DBDirectClient _client;
 
35
 
 
36
    /**
 
37
     * Test fixture for a write locked test using collection _ns.  Includes functionality to
 
38
     * partially construct a new IndexDetails in a manner that supports proper cleanup in
 
39
     * dropCollection().
 
40
     */
 
41
    class IndexBuildBase {
 
42
    public:
 
43
        IndexBuildBase() :
 
44
            _ctx( _ns ) {
 
45
            _client.createCollection( _ns );
 
46
        }
 
47
        ~IndexBuildBase() {
 
48
            _client.dropCollection( _ns );
 
49
            killCurrentOp.reset();
 
50
        }
 
51
    protected:
 
52
        /** @return IndexDetails for a new index on a:1, with the info field populated. */
 
53
        IndexDetails& addIndexWithInfo() {
 
54
            BSONObj indexInfo = BSON( "v" << 1 <<
 
55
                                      "key" << BSON( "a" << 1 ) <<
 
56
                                      "ns" << _ns <<
 
57
                                      "name" << "a_1" );
 
58
            int32_t lenWHdr = indexInfo.objsize() + Record::HeaderSize;
 
59
            const char* systemIndexes = "unittests.system.indexes";
 
60
            DiskLoc infoLoc = allocateSpaceForANewRecord( systemIndexes,
 
61
                                                          nsdetails( systemIndexes ),
 
62
                                                          lenWHdr,
 
63
                                                          false );
 
64
            Record* infoRecord = reinterpret_cast<Record*>( getDur().writingPtr( infoLoc.rec(),
 
65
                                                                                 lenWHdr ) );
 
66
            memcpy( infoRecord->data(), indexInfo.objdata(), indexInfo.objsize() );
 
67
            addRecordToRecListInExtent( infoRecord, infoLoc );
 
68
            IndexDetails& id = nsdetails( _ns )->getNextIndexDetails( _ns );
 
69
            nsdetails( _ns )->addIndex( _ns );
 
70
            id.info.writing() = infoLoc;
 
71
            return id;
 
72
        }
 
73
    private:
 
74
        Client::WriteContext _ctx;
 
75
    };
 
76
 
 
77
    /** addKeysToPhaseOne() adds keys from a collection's documents to an external sorter. */
 
78
    class AddKeysToPhaseOne : public IndexBuildBase {
 
79
    public:
 
80
        void run() {
 
81
            // Add some data to the collection.
 
82
            int32_t nDocs = 130;
 
83
            for( int32_t i = 0; i < nDocs; ++i ) {
 
84
                _client.insert( _ns, BSON( "a" << i ) );
 
85
            }
 
86
            IndexDetails& id = addIndexWithInfo();
 
87
            // Create a SortPhaseOne.
 
88
            SortPhaseOne phaseOne;
 
89
            ProgressMeterHolder pm (cc().curop()->setMessage("AddKeysToPhaseOne",
 
90
                                                             "AddKeysToPhaseOne Progress",
 
91
                                                             nDocs,
 
92
                                                             nDocs));
 
93
            // Add keys to phaseOne.
 
94
            addKeysToPhaseOne( _ns, id, BSON( "a" << 1 ), &phaseOne, nDocs, pm.get(), true );
 
95
            // Keys for all documents were added to phaseOne.
 
96
            ASSERT_EQUALS( static_cast<uint64_t>( nDocs ), phaseOne.n );
 
97
        }
 
98
    };
 
99
 
 
100
    /** addKeysToPhaseOne() aborts if the current operation is killed. */
 
101
    class InterruptAddKeysToPhaseOne : public IndexBuildBase {
 
102
    public:
 
103
        InterruptAddKeysToPhaseOne( bool mayInterrupt ) :
 
104
            _mayInterrupt( mayInterrupt ) {
 
105
        }
 
106
        void run() {
 
107
            // It's necessary to index sufficient keys that a RARELY condition will be triggered.
 
108
            int32_t nDocs = 130;
 
109
            // Add some data to the collection.
 
110
            for( int32_t i = 0; i < nDocs; ++i ) {
 
111
                _client.insert( _ns, BSON( "a" << i ) );
 
112
            }
 
113
            IndexDetails& id = addIndexWithInfo();
 
114
            // Create a SortPhaseOne.
 
115
            SortPhaseOne phaseOne;
 
116
            ProgressMeterHolder pm (cc().curop()->setMessage("InterruptAddKeysToPhaseOne",
 
117
                                                             "InterruptAddKeysToPhaseOne Progress",
 
118
                                                             nDocs,
 
119
                                                             nDocs));
 
120
            // Register a request to kill the current operation.
 
121
            cc().curop()->kill();
 
122
            if ( _mayInterrupt ) {
 
123
                // Add keys to phaseOne.
 
124
                ASSERT_THROWS( addKeysToPhaseOne( _ns,
 
125
                                                  id,
 
126
                                                  BSON( "a" << 1 ),
 
127
                                                  &phaseOne,
 
128
                                                  nDocs,
 
129
                                                  pm.get(),
 
130
                                                  _mayInterrupt ),
 
131
                               UserException );
 
132
                // Not all keys were added to phaseOne due to the interrupt.
 
133
                ASSERT( static_cast<uint64_t>( nDocs ) > phaseOne.n );
 
134
            }
 
135
            else {
 
136
                // Add keys to phaseOne.
 
137
                addKeysToPhaseOne( _ns,
 
138
                                   id,
 
139
                                   BSON( "a" << 1 ),
 
140
                                   &phaseOne,
 
141
                                   nDocs,
 
142
                                   pm.get(),
 
143
                                   _mayInterrupt );
 
144
                // All keys were added to phaseOne despite to the kill request, because
 
145
                // mayInterrupt == false.
 
146
                ASSERT_EQUALS( static_cast<uint64_t>( nDocs ), phaseOne.n );
 
147
            }
 
148
        }
 
149
    private:
 
150
        bool _mayInterrupt;
 
151
    };
 
152
 
 
153
    /** buildBottomUpPhases2And3() builds a btree from the keys in an external sorter. */
 
154
    class BuildBottomUp : public IndexBuildBase {
 
155
    public:
 
156
        void run() {
 
157
            IndexDetails& id = addIndexWithInfo();
 
158
            // Create a SortPhaseOne.
 
159
            SortPhaseOne phaseOne;
 
160
            phaseOne.sorter.reset( new BSONObjExternalSorter( id.idxInterface(),
 
161
                                                              BSON( "a" << 1 ) ) );
 
162
            // Add index keys to the phaseOne.
 
163
            int32_t nKeys = 130;
 
164
            for( int32_t i = 0; i < nKeys; ++i ) {
 
165
                phaseOne.sorter->add( BSON( "a" << i ), /* dummy disk loc */ DiskLoc(), false );
 
166
            }
 
167
            phaseOne.nkeys = phaseOne.n = nKeys;
 
168
            phaseOne.sorter->sort( false );
 
169
            // Set up remaining arguments.
 
170
            set<DiskLoc> dups;
 
171
            CurOp* op = cc().curop();
 
172
            ProgressMeterHolder pm (op->setMessage("BuildBottomUp",
 
173
                                                   "BuildBottomUp Progress",
 
174
                                                   nKeys,
 
175
                                                   nKeys));
 
176
            pm.finished();
 
177
            Timer timer;
 
178
            // The index's root has not yet been set.
 
179
            ASSERT( id.head.isNull() );
 
180
            // Finish building the index.
 
181
            buildBottomUpPhases2And3<V1>( true,
 
182
                                          id,
 
183
                                          *phaseOne.sorter,
 
184
                                          false,
 
185
                                          dups,
 
186
                                          op,
 
187
                                          &phaseOne,
 
188
                                          pm,
 
189
                                          timer,
 
190
                                          true );
 
191
            // The index's root is set after the build is complete.
 
192
            ASSERT( !id.head.isNull() );
 
193
            // Create a cursor over the index.
 
194
            scoped_ptr<BtreeCursor> cursor(
 
195
                    BtreeCursor::make( nsdetails( _ns ),
 
196
                                       id,
 
197
                                       BSON( "" << -1 ),    // startKey below minimum key.
 
198
                                       BSON( "" << nKeys ), // endKey above maximum key.
 
199
                                       true,                // endKeyInclusive true.
 
200
                                       1                    // direction forward.
 
201
                                       ) );
 
202
            // Check that the keys in the index are the expected ones.
 
203
            int32_t expectedKey = 0;
 
204
            for( ; cursor->ok(); cursor->advance(), ++expectedKey ) {
 
205
                ASSERT_EQUALS( expectedKey, cursor->currKey().firstElement().number() );
 
206
            }
 
207
            ASSERT_EQUALS( nKeys, expectedKey );
 
208
        }
 
209
    };
 
210
 
 
211
    /** buildBottomUpPhases2And3() aborts if the current operation is interrupted. */
 
212
    class InterruptBuildBottomUp : public IndexBuildBase {
 
213
    public:
 
214
        InterruptBuildBottomUp( bool mayInterrupt ) :
 
215
            _mayInterrupt( mayInterrupt ) {
 
216
        }
 
217
        void run() {
 
218
            IndexDetails& id = addIndexWithInfo();
 
219
            // Create a SortPhaseOne.
 
220
            SortPhaseOne phaseOne;
 
221
            phaseOne.sorter.reset( new BSONObjExternalSorter( id.idxInterface(),
 
222
                                                              BSON( "a" << 1 ) ) );
 
223
            // It's necessary to index sufficient keys that a RARELY condition will be triggered,
 
224
            // but few enough keys that the btree builder will not create an internal node and check
 
225
            // for an interrupt internally (which would cause this test to pass spuriously).
 
226
            int32_t nKeys = 130;
 
227
            // Add index keys to the phaseOne.
 
228
            for( int32_t i = 0; i < nKeys; ++i ) {
 
229
                phaseOne.sorter->add( BSON( "a" << i ), /* dummy disk loc */ DiskLoc(), false );
 
230
            }
 
231
            phaseOne.nkeys = phaseOne.n = nKeys;
 
232
            phaseOne.sorter->sort( false );
 
233
            // Set up remaining arguments.
 
234
            set<DiskLoc> dups;
 
235
            CurOp* op = cc().curop();
 
236
            ProgressMeterHolder pm (op->setMessage("InterruptBuildBottomUp",
 
237
                                                   "InterruptBuildBottomUp Progress",
 
238
                                                   nKeys,
 
239
                                                   nKeys));
 
240
            pm.finished();
 
241
            Timer timer;
 
242
            // The index's root has not yet been set.
 
243
            ASSERT( id.head.isNull() );
 
244
            // Register a request to kill the current operation.
 
245
            cc().curop()->kill();
 
246
            if ( _mayInterrupt ) {
 
247
                // The build is aborted due to the kill request.
 
248
                ASSERT_THROWS
 
249
                        ( buildBottomUpPhases2And3<V1>( true,
 
250
                                                        id,
 
251
                                                        *phaseOne.sorter,
 
252
                                                        false,
 
253
                                                        dups,
 
254
                                                        op,
 
255
                                                        &phaseOne,
 
256
                                                        pm,
 
257
                                                        timer,
 
258
                                                        _mayInterrupt ),
 
259
                          UserException );
 
260
                // The root of the index is not set because the build did not complete.
 
261
                ASSERT( id.head.isNull() );
 
262
            }
 
263
            else {
 
264
                // The build is aborted despite the kill request because mayInterrupt == false.
 
265
                buildBottomUpPhases2And3<V1>( true,
 
266
                                              id,
 
267
                                              *phaseOne.sorter,
 
268
                                              false,
 
269
                                              dups,
 
270
                                              op,
 
271
                                              &phaseOne,
 
272
                                              pm,
 
273
                                              timer,
 
274
                                              _mayInterrupt );
 
275
                // The index's root is set after the build is complete.
 
276
                ASSERT( !id.head.isNull() );
 
277
            }
 
278
        }
 
279
    private:
 
280
        bool _mayInterrupt;
 
281
    };
 
282
 
 
283
    /** doDropDups() deletes the duplicate documents in the provided set. */
 
284
    class DoDropDups : public IndexBuildBase {
 
285
    public:
 
286
        void run() {
 
287
            // Insert some documents.
 
288
            int32_t nDocs = 1000;
 
289
            for( int32_t i = 0; i < nDocs; ++i ) {
 
290
                _client.insert( _ns, BSON( "a" << ( i / 4 ) ) );
 
291
            }
 
292
            // Find the documents that are dups.
 
293
            set<DiskLoc> dups;
 
294
            int32_t last = -1;
 
295
            for( boost::shared_ptr<Cursor> cursor = theDataFileMgr.findAll( _ns );
 
296
                 cursor->ok();
 
297
                 cursor->advance() ) {
 
298
                int32_t currA = cursor->current()[ "a" ].Int();
 
299
                if ( currA == last ) {
 
300
                    dups.insert( cursor->currLoc() );
 
301
                }
 
302
                last = currA;
 
303
            }
 
304
            // Check the expected number of dups.
 
305
            ASSERT_EQUALS( static_cast<uint32_t>( nDocs / 4 * 3 ), dups.size() );
 
306
            // Drop the dups.
 
307
            doDropDups( _ns, nsdetails( _ns ), dups, true );
 
308
            // Check that the expected number of documents remain.
 
309
            ASSERT_EQUALS( static_cast<uint32_t>( nDocs / 4 ), _client.count( _ns ) );
 
310
        }
 
311
    };
 
312
 
 
313
    /** doDropDups() aborts if the current operation is interrupted. */
 
314
    class InterruptDoDropDups : public IndexBuildBase {
 
315
    public:
 
316
        InterruptDoDropDups( bool mayInterrupt ) :
 
317
            _mayInterrupt( mayInterrupt ) {
 
318
        }
 
319
        void run() {
 
320
            // Insert some documents.
 
321
            int32_t nDocs = 1000;
 
322
            for( int32_t i = 0; i < nDocs; ++i ) {
 
323
                _client.insert( _ns, BSON( "a" << ( i / 4 ) ) );
 
324
            }
 
325
            // Find the documents that are dups.
 
326
            set<DiskLoc> dups;
 
327
            int32_t last = -1;
 
328
            for( boost::shared_ptr<Cursor> cursor = theDataFileMgr.findAll( _ns );
 
329
                 cursor->ok();
 
330
                 cursor->advance() ) {
 
331
                int32_t currA = cursor->current()[ "a" ].Int();
 
332
                if ( currA == last ) {
 
333
                    dups.insert( cursor->currLoc() );
 
334
                }
 
335
                last = currA;
 
336
            }
 
337
            // Check the expected number of dups.  There must be enough to trigger a RARELY
 
338
            // condition when deleting them.
 
339
            ASSERT_EQUALS( static_cast<uint32_t>( nDocs / 4 * 3 ), dups.size() );
 
340
            // Kill the current operation.
 
341
            cc().curop()->kill();
 
342
            if ( _mayInterrupt ) {
 
343
                // doDropDups() aborts.
 
344
                ASSERT_THROWS( doDropDups( _ns, nsdetails( _ns ), dups, _mayInterrupt ),
 
345
                               UserException );
 
346
                // Not all dups are dropped.
 
347
                ASSERT( static_cast<uint32_t>( nDocs / 4 ) < _client.count( _ns ) );
 
348
            }
 
349
            else {
 
350
                // doDropDups() succeeds.
 
351
                doDropDups( _ns, nsdetails( _ns ), dups, _mayInterrupt );
 
352
                // The expected number of documents were dropped.
 
353
                ASSERT_EQUALS( static_cast<uint32_t>( nDocs / 4 ), _client.count( _ns ) );
 
354
            }
 
355
        }
 
356
    private:
 
357
        bool _mayInterrupt;
 
358
    };
 
359
 
 
360
    /** DataFileMgr::insertWithObjMod is killed if mayInterrupt is true. */
 
361
    class InsertBuildIndexInterrupt : public IndexBuildBase {
 
362
    public:
 
363
        void run() {
 
364
            // Insert some documents.
 
365
            int32_t nDocs = 1000;
 
366
            for( int32_t i = 0; i < nDocs; ++i ) {
 
367
                _client.insert( _ns, BSON( "a" << i ) );
 
368
            }
 
369
            // Initialize curop.
 
370
            cc().curop()->reset();
 
371
            // Request an interrupt.
 
372
            killCurrentOp.killAll();
 
373
            BSONObj indexInfo = BSON( "key" << BSON( "a" << 1 ) << "ns" << _ns << "name" << "a_1" );
 
374
            // The call is interrupted because mayInterrupt == true.
 
375
            ASSERT_THROWS( theDataFileMgr.insertWithObjMod( "unittests.system.indexes",
 
376
                                                            indexInfo,
 
377
                                                            true ),
 
378
                           UserException );
 
379
            // The new index is not listed in system.indexes because the index build failed.
 
380
            ASSERT_EQUALS( 0U,
 
381
                           _client.count( "unittests.system.indexes",
 
382
                                          BSON( "ns" << _ns << "name" << "a_1" ) ) );
 
383
        }
 
384
    };
 
385
 
 
386
    /** DataFileMgr::insertWithObjMod is not killed if mayInterrupt is false. */
 
387
    class InsertBuildIndexInterruptDisallowed : public IndexBuildBase {
 
388
    public:
 
389
        void run() {
 
390
            // Insert some documents.
 
391
            int32_t nDocs = 1000;
 
392
            for( int32_t i = 0; i < nDocs; ++i ) {
 
393
                _client.insert( _ns, BSON( "a" << i ) );
 
394
            }
 
395
            // Initialize curop.
 
396
            cc().curop()->reset();
 
397
            // Request an interrupt.
 
398
            killCurrentOp.killAll();
 
399
            BSONObj indexInfo = BSON( "key" << BSON( "a" << 1 ) << "ns" << _ns << "name" << "a_1" );
 
400
            // The call is not interrupted because mayInterrupt == false.
 
401
            theDataFileMgr.insertWithObjMod( "unittests.system.indexes", indexInfo, false );
 
402
            // The new index is listed in system.indexes because the index build completed.
 
403
            ASSERT_EQUALS( 1U,
 
404
                           _client.count( "unittests.system.indexes",
 
405
                                          BSON( "ns" << _ns << "name" << "a_1" ) ) );
 
406
        }
 
407
    };
 
408
 
 
409
    /** DataFileMgr::insertWithObjMod is killed when building the _id index. */
 
410
    class InsertBuildIdIndexInterrupt : public IndexBuildBase {
 
411
    public:
 
412
        void run() {
 
413
            // Recreate the collection as capped, without an _id index.
 
414
            _client.dropCollection( _ns );
 
415
            BSONObj info;
 
416
            ASSERT( _client.runCommand( "unittests",
 
417
                                        BSON( "create" << "indexupdate" <<
 
418
                                              "capped" << true <<
 
419
                                              "size" << ( 10 * 1024 ) <<
 
420
                                              "autoIndexId" << false ),
 
421
                                        info ) );
 
422
            // Insert some documents.
 
423
            int32_t nDocs = 1000;
 
424
            for( int32_t i = 0; i < nDocs; ++i ) {
 
425
                _client.insert( _ns, BSON( "_id" << i ) );
 
426
            }
 
427
            // Initialize curop.
 
428
            cc().curop()->reset();
 
429
            // Request an interrupt.
 
430
            killCurrentOp.killAll();
 
431
            BSONObj indexInfo = BSON( "key" << BSON( "_id" << 1 ) <<
 
432
                                      "ns" << _ns <<
 
433
                                      "name" << "_id" );
 
434
            // The call is interrupted because mayInterrupt == true.
 
435
            ASSERT_THROWS( theDataFileMgr.insertWithObjMod( "unittests.system.indexes",
 
436
                                                            indexInfo,
 
437
                                                            true ),
 
438
                           UserException );
 
439
            // The new index is not listed in system.indexes because the index build failed.
 
440
            ASSERT_EQUALS( 0U, _client.count( "unittests.system.indexes", BSON( "ns" << _ns ) ) );
 
441
        }
 
442
    };
 
443
 
 
444
    /**
 
445
     * DataFileMgr::insertWithObjMod is not killed when building the _id index if mayInterrupt is
 
446
     * false.
 
447
     */
 
448
    class InsertBuildIdIndexInterruptDisallowed : public IndexBuildBase {
 
449
    public:
 
450
        void run() {
 
451
            // Recreate the collection as capped, without an _id index.
 
452
            _client.dropCollection( _ns );
 
453
            BSONObj info;
 
454
            ASSERT( _client.runCommand( "unittests",
 
455
                                        BSON( "create" << "indexupdate" <<
 
456
                                              "capped" << true <<
 
457
                                              "size" << ( 10 * 1024 ) <<
 
458
                                              "autoIndexId" << false ),
 
459
                                        info ) );
 
460
            // Insert some documents.
 
461
            int32_t nDocs = 1000;
 
462
            for( int32_t i = 0; i < nDocs; ++i ) {
 
463
                _client.insert( _ns, BSON( "_id" << i ) );
 
464
            }
 
465
            // Initialize curop.
 
466
            cc().curop()->reset();
 
467
            // Request an interrupt.
 
468
            killCurrentOp.killAll();
 
469
            BSONObj indexInfo = BSON( "key" << BSON( "_id" << 1 ) <<
 
470
                                      "ns" << _ns <<
 
471
                                      "name" << "_id" );
 
472
            // The call is not interrupted because mayInterrupt == false.
 
473
            theDataFileMgr.insertWithObjMod( "unittests.system.indexes", indexInfo, false );
 
474
            // The new index is listed in system.indexes because the index build succeeded.
 
475
            ASSERT_EQUALS( 1U, _client.count( "unittests.system.indexes", BSON( "ns" << _ns ) ) );
 
476
        }
 
477
    };
 
478
 
 
479
    /** DBDirectClient::ensureIndex() is not interrupted. */
 
480
    class DirectClientEnsureIndexInterruptDisallowed : public IndexBuildBase {
 
481
    public:
 
482
        void run() {
 
483
            // Insert some documents.
 
484
            int32_t nDocs = 1000;
 
485
            for( int32_t i = 0; i < nDocs; ++i ) {
 
486
                _client.insert( _ns, BSON( "a" << i ) );
 
487
            }
 
488
            // Initialize curop.
 
489
            cc().curop()->reset();
 
490
            // Request an interrupt.  killAll() rather than kill() is required because the direct
 
491
            // client will build the index using a new opid.
 
492
            killCurrentOp.killAll();
 
493
            // The call is not interrupted.
 
494
            _client.ensureIndex( _ns, BSON( "a" << 1 ) );
 
495
            // The new index is listed in system.indexes because the index build completed.
 
496
            ASSERT_EQUALS( 1U,
 
497
                           _client.count( "unittests.system.indexes",
 
498
                                          BSON( "ns" << _ns << "name" << "a_1" ) ) );
 
499
        }
 
500
    };
 
501
 
 
502
    /** Helpers::ensureIndex() is not interrupted. */
 
503
    class HelpersEnsureIndexInterruptDisallowed : public IndexBuildBase {
 
504
    public:
 
505
        void run() {
 
506
            // Insert some documents.
 
507
            int32_t nDocs = 1000;
 
508
            for( int32_t i = 0; i < nDocs; ++i ) {
 
509
                _client.insert( _ns, BSON( "a" << i ) );
 
510
            }
 
511
            // Initialize curop.
 
512
            cc().curop()->reset();
 
513
            // Request an interrupt.
 
514
            killCurrentOp.killAll();
 
515
            // The call is not interrupted.
 
516
            Helpers::ensureIndex( _ns, BSON( "a" << 1 ), false, "a_1" );
 
517
            // The new index is listed in system.indexes because the index build completed.
 
518
            ASSERT_EQUALS( 1U,
 
519
                           _client.count( "unittests.system.indexes",
 
520
                                          BSON( "ns" << _ns << "name" << "a_1" ) ) );
 
521
        }
 
522
    };
 
523
 
 
524
    class IndexBuildInProgressTest : public IndexBuildBase {
 
525
    public:
 
526
        void run() {
 
527
            // _id_ is at 0, so nIndexes == 1
 
528
            halfAddIndex("a");
 
529
            halfAddIndex("b");
 
530
            halfAddIndex("c");
 
531
            halfAddIndex("d");
 
532
            int offset = IndexBuildsInProgress::get(_ns, "b_1");
 
533
            ASSERT_EQUALS(2, offset);
 
534
 
 
535
            IndexBuildsInProgress::remove(_ns, offset);
 
536
            nsdetails(_ns)->indexBuildsInProgress--;
 
537
 
 
538
            ASSERT_EQUALS(2, IndexBuildsInProgress::get(_ns, "c_1"));
 
539
            ASSERT_EQUALS(3, IndexBuildsInProgress::get(_ns, "d_1"));
 
540
 
 
541
            offset = IndexBuildsInProgress::get(_ns, "d_1");
 
542
            IndexBuildsInProgress::remove(_ns, offset);
 
543
            nsdetails(_ns)->indexBuildsInProgress--;
 
544
 
 
545
            ASSERT_EQUALS(2, IndexBuildsInProgress::get(_ns, "c_1"));
 
546
            ASSERT_EQUALS(-1, IndexBuildsInProgress::get(_ns, "d_1"));
 
547
 
 
548
            offset = IndexBuildsInProgress::get(_ns, "a_1");
 
549
            IndexBuildsInProgress::remove(_ns, offset);
 
550
            nsdetails(_ns)->indexBuildsInProgress--;
 
551
 
 
552
            ASSERT_EQUALS(1, IndexBuildsInProgress::get(_ns, "c_1"));
 
553
        }
 
554
 
 
555
    private:
 
556
        IndexDetails& halfAddIndex(const std::string& key) {
 
557
            BSONObj indexInfo = BSON( "v" << 1 <<
 
558
                                      "key" << BSON( key << 1 ) <<
 
559
                                      "ns" << _ns <<
 
560
                                      "name" << (key+"_1"));
 
561
            int32_t lenWHdr = indexInfo.objsize() + Record::HeaderSize;
 
562
            const char* systemIndexes = "unittests.system.indexes";
 
563
            DiskLoc infoLoc = allocateSpaceForANewRecord( systemIndexes,
 
564
                                                          nsdetails( systemIndexes ),
 
565
                                                          lenWHdr,
 
566
                                                          false );
 
567
            Record* infoRecord = reinterpret_cast<Record*>( getDur().writingPtr( infoLoc.rec(),
 
568
                                                                                 lenWHdr ) );
 
569
            memcpy( infoRecord->data(), indexInfo.objdata(), indexInfo.objsize() );
 
570
            addRecordToRecListInExtent( infoRecord, infoLoc );
 
571
            IndexDetails& id = nsdetails( _ns )->getNextIndexDetails( _ns );
 
572
            id.info = infoLoc;
 
573
            nsdetails(_ns)->indexBuildsInProgress++;
 
574
 
 
575
            return id;
 
576
        }
 
577
    };
 
578
 
 
579
    class IndexUpdateTests : public Suite {
 
580
    public:
 
581
        IndexUpdateTests() :
 
582
            Suite( "indexupdate" ) {
 
583
        }
 
584
 
 
585
        void setupTests() {
 
586
            add<AddKeysToPhaseOne>();
 
587
            add<InterruptAddKeysToPhaseOne>( false );
 
588
            add<InterruptAddKeysToPhaseOne>( true );
 
589
            add<BuildBottomUp>();
 
590
            add<InterruptBuildBottomUp>( false );
 
591
            add<InterruptBuildBottomUp>( true );
 
592
            add<DoDropDups>();
 
593
            add<InterruptDoDropDups>( false );
 
594
            add<InterruptDoDropDups>( true );
 
595
            add<InsertBuildIndexInterrupt>();
 
596
            add<InsertBuildIndexInterruptDisallowed>();
 
597
            add<InsertBuildIdIndexInterrupt>();
 
598
            add<InsertBuildIdIndexInterruptDisallowed>();
 
599
            add<DirectClientEnsureIndexInterruptDisallowed>();
 
600
            add<HelpersEnsureIndexInterruptDisallowed>();
 
601
            add<IndexBuildInProgressTest>();
 
602
        }
 
603
    } indexUpdateTests;
 
604
 
 
605
} // namespace IndexUpdateTests