예제 #1
0
        void run() {
            Client::WriteContext ctx(&_txn, ns());

            Database* db = ctx.ctx().db();
            Collection* coll = db->getCollection(&_txn, ns());
            if (!coll) {
                WriteUnitOfWork wuow(&_txn);
                coll = db->createCollection(&_txn, ns());
                wuow.commit();
            }
            WorkingSet ws;

            std::set<WorkingSetID> expectedResultIds;
            std::set<WorkingSetID> resultIds;

            // Create a KeepMutationsStage with an EOF child, and flag 50 objects.  We expect these
            // objects to be returned by the KeepMutationsStage.
            MatchExpression* nullFilter = NULL;
            std::auto_ptr<KeepMutationsStage> keep(new KeepMutationsStage(nullFilter, &ws,
                                                                          new EOFStage()));
            for (size_t i = 0; i < 50; ++i) {
                WorkingSetID id = ws.allocate();
                WorkingSetMember* member = ws.get(id);
                member->state = WorkingSetMember::OWNED_OBJ;
                member->obj = BSON("x" << 1);
                ws.flagForReview(id);
                expectedResultIds.insert(id);
            }

            // Call work() on the KeepMutationsStage.  The stage should start streaming the
            // already-flagged objects.
            WorkingSetID id = getNextResult(keep.get());
            resultIds.insert(id);

            // Flag more objects, then call work() again on the KeepMutationsStage, and expect none
            // of the newly-flagged objects to be returned (the KeepMutationsStage does not
            // incorporate objects flagged since the streaming phase started).
            //
            // This condition triggers SERVER-15580 (the new flagging causes a rehash of the
            // unordered_set "WorkingSet::_flagged", which invalidates all iterators, which were
            // previously being dereferenced in KeepMutationsStage::work()).
            // Note that std::unordered_set<>::insert() triggers a rehash if the new number of
            // elements is greater than or equal to max_load_factor()*bucket_count().
            size_t rehashSize = static_cast<size_t>(ws.getFlagged().max_load_factor() *
                                                    ws.getFlagged().bucket_count());
            while (ws.getFlagged().size() <= rehashSize) {
                WorkingSetID id = ws.allocate();
                WorkingSetMember* member = ws.get(id);
                member->state = WorkingSetMember::OWNED_OBJ;
                member->obj = BSON("x" << 1);
                ws.flagForReview(id);
            }
            while ((id = getNextResult(keep.get())) != WorkingSet::INVALID_ID) {
                resultIds.insert(id);
            }

            // Assert that only the first 50 objects were returned.
            ASSERT(expectedResultIds == resultIds);
        }
예제 #2
0
    void run() {
        Client::WriteContext ctx(ns());
        Database* db = ctx.ctx().db();
        Collection* coll = db->getCollection(ns());
        if (!coll) {
            coll = db->createCollection(ns());
        }

        for (int i = 0; i < 50; ++i) {
            insert(BSON("foo" << i << "bar" << i));
        }

        addIndex(BSON("foo" << 1));
        addIndex(BSON("bar" << 1));

        WorkingSet ws;
        scoped_ptr<AndHashStage> ah(new AndHashStage(&ws, NULL));

        // Foo <= 20
        IndexScanParams params;
        params.descriptor = getIndex(BSON("foo" << 1), coll);
        params.bounds.isSimpleRange = true;
        params.bounds.startKey = BSON("" << 20);
        params.bounds.endKey = BSONObj();
        params.bounds.endKeyInclusive = true;
        params.direction = -1;
        ah->addChild(new IndexScan(params, &ws, NULL));

        // Bar >= 10
        params.descriptor = getIndex(BSON("bar" << 1), coll);
        params.bounds.startKey = BSON("" << 10);
        params.bounds.endKey = BSONObj();
        params.bounds.endKeyInclusive = true;
        params.direction = 1;
        ah->addChild(new IndexScan(params, &ws, NULL));

        // ah reads the first child into its hash table.
        // ah should read foo=20, foo=19, ..., foo=0 in that order.
        // Read half of them...
        for (int i = 0; i < 10; ++i) {
            WorkingSetID out;
            PlanStage::StageState status = ah->work(&out);
            ASSERT_EQUALS(PlanStage::NEED_TIME, status);
        }

        // ...yield
        ah->prepareToYield();
        // ...invalidate one of the read objects
        set<DiskLoc> data;
        getLocs(&data, coll);
        for (set<DiskLoc>::const_iterator it = data.begin(); it != data.end(); ++it) {
            if (it->obj()["foo"].numberInt() == 15) {
                ah->invalidate(*it);
                remove(it->obj());
                break;
            }
        }
        ah->recoverFromYield();

        // And expect to find foo==15 it flagged for review.
        const unordered_set<WorkingSetID>& flagged = ws.getFlagged();
        ASSERT_EQUALS(size_t(1), flagged.size());

        // Expect to find the right value of foo in the flagged item.
        WorkingSetMember* member = ws.get(*flagged.begin());
        ASSERT_TRUE(NULL != member);
        ASSERT_EQUALS(WorkingSetMember::OWNED_OBJ, member->state);
        BSONElement elt;
        ASSERT_TRUE(member->getFieldDotted("foo", &elt));
        ASSERT_EQUALS(15, elt.numberInt());

        // Now, finish up the AND.  Since foo == bar, we would have 11 results, but we subtract
        // one because of a mid-plan invalidation, so 10.
        int count = 0;
        while (!ah->isEOF()) {
            WorkingSetID id;
            PlanStage::StageState status = ah->work(&id);
            if (PlanStage::ADVANCED != status) {
                continue;
            }

            ++count;
            member = ws.get(id);

            ASSERT_TRUE(member->getFieldDotted("foo", &elt));
            ASSERT_LESS_THAN_OR_EQUALS(elt.numberInt(), 20);
            ASSERT_NOT_EQUALS(15, elt.numberInt());
            ASSERT_TRUE(member->getFieldDotted("bar", &elt));
            ASSERT_GREATER_THAN_OR_EQUALS(elt.numberInt(), 10);
        }

        ASSERT_EQUALS(10, count);
    }
예제 #3
0
    void run() {
        Client::WriteContext ctx(ns());
        Database* db = ctx.ctx().db();
        Collection* coll = db->getCollection(ns());
        if (!coll) {
            coll = db->createCollection(ns());
        }

        // Insert a bunch of data
        for (int i = 0; i < 50; ++i) {
            insert(BSON("foo" << 1 << "bar" << 1));
        }
        addIndex(BSON("foo" << 1));
        addIndex(BSON("bar" << 1));

        WorkingSet ws;
        scoped_ptr<AndSortedStage> ah(new AndSortedStage(&ws, NULL));

        // Scan over foo == 1
        IndexScanParams params;
        params.descriptor = getIndex(BSON("foo" << 1), coll);
        params.bounds.isSimpleRange = true;
        params.bounds.startKey = BSON("" << 1);
        params.bounds.endKey = BSON("" << 1);
        params.bounds.endKeyInclusive = true;
        params.direction = 1;
        ah->addChild(new IndexScan(params, &ws, NULL));

        // Scan over bar == 1
        params.descriptor = getIndex(BSON("bar" << 1), coll);
        ah->addChild(new IndexScan(params, &ws, NULL));

        // Get the set of disklocs in our collection to use later.
        set<DiskLoc> data;
        getLocs(&data, coll);

        // We're making an assumption here that happens to be true because we clear out the
        // collection before running this: increasing inserts have increasing DiskLocs.
        // This isn't true in general if the collection is not dropped beforehand.
        WorkingSetID id;

        // Sorted AND looks at the first child, which is an index scan over foo==1.
        ah->work(&id);

        // The first thing that the index scan returns (due to increasing DiskLoc trick) is the
        // very first insert, which should be the very first thing in data.  Let's invalidate it
        // and make sure it shows up in the flagged results.
        ah->prepareToYield();
        ah->invalidate(*data.begin());
        remove(data.begin()->obj());
        ah->recoverFromYield();

        // Make sure the nuked obj is actually in the flagged data.
        ASSERT_EQUALS(ws.getFlagged().size(), size_t(1));
        WorkingSetMember* member = ws.get(*ws.getFlagged().begin());
        ASSERT_EQUALS(WorkingSetMember::OWNED_OBJ, member->state);
        BSONElement elt;
        ASSERT_TRUE(member->getFieldDotted("foo", &elt));
        ASSERT_EQUALS(1, elt.numberInt());
        ASSERT_TRUE(member->getFieldDotted("bar", &elt));
        ASSERT_EQUALS(1, elt.numberInt());

        set<DiskLoc>::iterator it = data.begin();

        // Proceed along, AND-ing results.
        int count = 0;
        while (!ah->isEOF() && count < 10) {
            WorkingSetID id;
            PlanStage::StageState status = ah->work(&id);
            if (PlanStage::ADVANCED != status) {
                continue;
            }

            ++count;
            ++it;
            member = ws.get(id);

            ASSERT_TRUE(member->getFieldDotted("foo", &elt));
            ASSERT_EQUALS(1, elt.numberInt());
            ASSERT_TRUE(member->getFieldDotted("bar", &elt));
            ASSERT_EQUALS(1, elt.numberInt());
            ASSERT_EQUALS(member->loc, *it);
        }

        // Move 'it' to a result that's yet to show up.
        for (int i = 0; i < count + 10; ++i) {
            ++it;
        }
        // Remove a result that's coming up.  It's not the 'target' result of the AND so it's
        // not flagged.
        ah->prepareToYield();
        ah->invalidate(*it);
        remove(it->obj());
        ah->recoverFromYield();

        // Get all results aside from the two we killed.
        while (!ah->isEOF()) {
            WorkingSetID id;
            PlanStage::StageState status = ah->work(&id);
            if (PlanStage::ADVANCED != status) {
                continue;
            }

            ++count;
            member = ws.get(id);

            ASSERT_TRUE(member->getFieldDotted("foo", &elt));
            ASSERT_EQUALS(1, elt.numberInt());
            ASSERT_TRUE(member->getFieldDotted("bar", &elt));
            ASSERT_EQUALS(1, elt.numberInt());
        }

        ASSERT_EQUALS(count, 48);

        ASSERT_EQUALS(size_t(1), ws.getFlagged().size());
    }
예제 #4
0
        void run() {
            Client::WriteContext ctx(ns());
            Database* db = ctx.ctx().db();
            Collection* coll = db->getCollection(ns());
            if (!coll) {
                coll = db->createCollection(ns());
            }

            for (int i = 0; i < 50; ++i) {
                insert(BSON("_id" << i << "foo" << i << "bar" << i << "baz" << i));
            }

            addIndex(BSON("foo" << 1));
            addIndex(BSON("bar" << 1));
            addIndex(BSON("baz" << 1));

            WorkingSet ws;
            scoped_ptr<AndHashStage> ah(new AndHashStage(&ws, NULL));

            // Foo <= 20 (descending)
            IndexScanParams params;
            params.descriptor = getIndex(BSON("foo" << 1), coll);
            params.bounds.isSimpleRange = true;
            params.bounds.startKey = BSON("" << 20);
            params.bounds.endKey = BSONObj();
            params.bounds.endKeyInclusive = true;
            params.direction = -1;
            ah->addChild(new IndexScan(params, &ws, NULL));

            // Bar <= 19 (descending)
            params.descriptor = getIndex(BSON("bar" << 1), coll);
            params.bounds.startKey = BSON("" << 19);
            ah->addChild(new IndexScan(params, &ws, NULL));

            // First call to work reads the first result from the children.
            // The first result is for the first scan over foo is {foo: 20, bar: 20, baz: 20}.
            // The first result is for the second scan over bar is {foo: 19, bar: 19, baz: 19}.
            WorkingSetID id;
            PlanStage::StageState status = ah->work(&id);
            ASSERT_EQUALS(PlanStage::NEED_TIME, status);

            const unordered_set<WorkingSetID>& flagged = ws.getFlagged();
            ASSERT_EQUALS(size_t(0), flagged.size());

            // "delete" deletedObj (by invalidating the DiskLoc of the obj that matches it).
            BSONObj deletedObj = BSON("_id" << 20 << "foo" << 20 << "bar" << 20 << "baz" << 20);
            ah->prepareToYield();
            set<DiskLoc> data;
            getLocs(&data, coll);
            for (set<DiskLoc>::const_iterator it = data.begin(); it != data.end(); ++it) {
                if (0 == deletedObj.woCompare(it->obj())) {
                    ah->invalidate(*it, INVALIDATION_DELETION);
                    break;
                }
            }
            ah->recoverFromYield();

            // The deleted obj should show up in flagged.
            ASSERT_EQUALS(size_t(1), flagged.size());

            // And not in our results.
            int count = 0;
            while (!ah->isEOF()) {
                WorkingSetID id;
                PlanStage::StageState status = ah->work(&id);
                if (PlanStage::ADVANCED != status) { continue; }
                WorkingSetMember* wsm = ws.get(id);
                ASSERT_NOT_EQUALS(0, deletedObj.woCompare(wsm->loc.obj()));
                ++count;
            }

            ASSERT_EQUALS(count, 20);
        }