summaryrefslogtreecommitdiff
path: root/jstests/sharding/lookup_from_config_cache_chunks.js
blob: 2b66b5504a411dfd34925acaee8c211d67399645 (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
/**
 * With standard $lookup syntax, the "from" collection is always interpreted to be in the same
 * database the aggregate command is run on. Additionally, the merging is always done on the primary
 * shard for the database.
 *
 * This tests alternative $lookup syntax that supports reading from an unsharded collection that has
 * identical contents across all shards (specifically config.cache.chunks.* namespaces). With the
 * alternative syntax, $lookup behavior changes and it is run locally on each shard rather than
 * merged on the primary shard for the database.
 *
 * Alternative $lookup syntax:
 *        {$lookup: {from: {db:<>, coll:<>},...}}
 *
 * @tags: [
 *   requires_fcv_47, disabled_due_to_server_58295
 * ]
 */
(function() {
"use strict";

load("jstests/aggregation/extras/utils.js");  // For assertErrorCode.
load("jstests/libs/discover_topology.js");    // For findNonConfigNodes.
load("jstests/libs/profiler.js");             // For profilerHasSingleMatchingEntryOrThrow.
load("jstests/libs/uuid_util.js");            // For extractUUIDFromObject.
// For flushRoutersAndRefreshShardMetadata.
load('jstests/sharding/libs/sharded_transactions_helpers.js');

const st = new ShardingTest({shards: 2});
const dbName = jsTestName();
const collName = "foo";
var chunksCollName;

const shard0DB = st.shard0.getDB(dbName);
const shard1DB = st.shard1.getDB(dbName);
const sourceCollection = st.s0.getDB(dbName)[collName];

// Sets up the data for $lookup on config.cache.chunks.* namespaces.
const setUp = () => {
    sourceCollection.drop();
    // Set up sourceCollection to be sharded on {x:1} and to have the following distribution:
    //      shard0: [MinKey, 0)
    //      shard1: [0, MaxKey)
    st.shardColl(sourceCollection, {x: 1}, {x: 0}, {x: 0}, dbName);

    // Insert a corresponding entry in sourceCollection for each document in
    // config.cache.chunks.collUuid.
    assert.commandWorked(sourceCollection.insert({x: MinKey}));
    assert.commandWorked(sourceCollection.insert({x: 0}));

    const collNs = dbName + "." + collName;
    const collEntry = st.config.collections.findOne({_id: collNs});
    chunksCollName = "cache.chunks." + collNs;
    flushRoutersAndRefreshShardMetadata(st, {collNs});
};

// $lookup alternative syntax only supports reading 'from' config.cache.chunks.* namespaces.
const invalidLookups = [
    {
        $lookup: {
            from: {db: "config", coll: "validDB.WithInvalid.collection"},
            localField: "x",
            foreignField: "_id.x",
            as: "results",
        }
    },
    {
        $lookup: {
            from: {db: "wrongDB", coll: `${chunksCollName}`},
            localField: "x",
            foreignField: "_id.x",
            as: "results",
        }
    },
    {
        $lookup: {
            from: {db: "config", coll: "validDB.LetLookup.invalidCollectionName"}, 
            let: {x_field: "$x"},
            pipeline: [
                {$match: {$expr: { $eq: ["$_id.x", "$$x_field"]}}}
            ],
            as: "results",
        }
    },
    {
        $lookup: {
            from: {db: "wrongDBWithLet", coll: `${chunksCollName}`}, 
            let: {x_field: "$x"},
            pipeline: [
                {$match: {$expr: { $eq: ["$_id.x", "$$x_field"]}}}
            ],
            as: "results",
        }
    }
];

invalidLookups.forEach((testCase) => {
    assertErrorCode(sourceCollection,
                    [testCase],
                    ErrorCodes.FailedToParse,
                    `Expected $lookup to fail. Original command: ${tojson(testCase)}`);
});

const nodeList = DiscoverTopology.findNonConfigNodes(st.s);

// Tests that $lookup from config.cache.chunks.* yields the expected results.
const testLookupFromConfigCacheChunks = (lookupAgg) => {
    const isShardedLookupEnabled = st.s.adminCommand({getParameter: 1, featureFlagShardedLookup: 1})
                                       .featureFlagShardedLookup.value;

    jsTestLog(`Running test on lookup: ${tojson(lookupAgg)} with featureFlagShardedLookup: ${
        isShardedLookupEnabled}`);

    const results = sourceCollection.aggregate(lookupAgg).toArray();
    results.forEach((res) => {
        assert.eq(res.results.length, 1, `Failed with results ${tojson(results)}`);
    });
};

setUp();
const lookupBasic = {
    $lookup: {
        from: {db: "config", coll: `${chunksCollName}`},
        localField: "x",
        foreignField: "_id.x",
        as: "results",
    }
};
testLookupFromConfigCacheChunks(lookupBasic);

const lookupLet = {
    $lookup: {
        from: {db: "config", coll: `${chunksCollName}`},
        let: {x_field: "$x"},
        pipeline: [
            {$match: {$expr: { $eq: ["$_id.x", "$$x_field"]}}}
        ],
        as: "results",
    }
};
testLookupFromConfigCacheChunks(lookupLet);
st.stop();
}());