summaryrefslogtreecommitdiff
path: root/jstests/sharding/time_zone_info_mongos.js
blob: 1f9bb51f88e68d828c1dd9c978028cc7d01de1fb (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
// Test that mongoS accepts --timeZoneInfo <timezoneDBPath> as a command-line argument and that an
// aggregation pipeline with timezone expressions executes correctly on mongoS.
(function() {
const tzGoodInfo = "jstests/libs/config_files/good_timezone_info";
const tzBadInfo = "jstests/libs/config_files/bad_timezone_info";
const tzNoInfo = "jstests/libs/config_files/missing_directory";

const st = new ShardingTest({
    shards: 2,
    mongos: {s0: {timeZoneInfo: tzGoodInfo}},
    rs: {nodes: 1, timeZoneInfo: tzGoodInfo}
});

const mongosDB = st.s0.getDB(jsTestName());
const mongosColl = mongosDB[jsTestName()];

assert.commandWorked(mongosDB.dropDatabase());

// Confirm that the timeZoneInfo command-line argument has been set on mongoS.
const mongosCfg = assert.commandWorked(mongosDB.adminCommand({getCmdLineOpts: 1}));
assert.eq(mongosCfg.parsed.processManagement.timeZoneInfo, tzGoodInfo);

// Test that a bad timezone file causes mongoS startup to fail.
let conn = MongoRunner.runMongos({configdb: st.configRS.getURL(), timeZoneInfo: tzBadInfo});
assert.eq(conn, null, "expected launching mongos with bad timezone rules to fail");
assert.neq(-1, rawMongoProgramOutput().search(/Fatal assertion.*40475/));

// Test that a non-existent timezone directory causes mongoS startup to fail.
conn = MongoRunner.runMongos({configdb: st.configRS.getURL(), timeZoneInfo: tzNoInfo});
assert.eq(conn, null, "expected launching mongos with bad timezone rules to fail");
// Look for either old or new error message
assert(rawMongoProgramOutput().includes("Error creating service context") ||
       rawMongoProgramOutput().includes("Failed to create service context"));

// Enable sharding on the test DB and ensure its primary is st.shard0.shardName.
assert.commandWorked(mongosDB.adminCommand({enableSharding: mongosDB.getName()}));
st.ensurePrimaryShard(mongosDB.getName(), st.rs0.getURL());

// Shard the test collection on _id.
assert.commandWorked(
    mongosDB.adminCommand({shardCollection: mongosColl.getFullName(), key: {_id: 1}}));

// Split the collection into 2 chunks: [MinKey, 0), [0, MaxKey).
assert.commandWorked(mongosDB.adminCommand({split: mongosColl.getFullName(), middle: {_id: 0}}));

// Move the [0, MaxKey) chunk to st.shard1.shardName.
assert.commandWorked(mongosDB.adminCommand(
    {moveChunk: mongosColl.getFullName(), find: {_id: 1}, to: st.rs1.getURL()}));

// Write a document containing a 'date' field to each chunk.
assert.commandWorked(mongosColl.insert({_id: -1, date: ISODate("2017-11-13T12:00:00.000+0000")}));
assert.commandWorked(mongosColl.insert({_id: 1, date: ISODate("2017-11-13T03:00:00.000+0600")}));

// Constructs a pipeline which splits the 'date' field into its constituent parts on mongoD,
// reassembles the original date on mongoS, and verifies that the two match. All timezone
// expressions in the pipeline use the passed 'tz' string or, if absent, default to "GMT".
function buildTimeZonePipeline(tz) {
    // We use $const here so that the input pipeline matches the format of the explain output.
    const tzExpr = {$const: (tz || "GMT")};
    return [
        {$addFields: {mongodParts: {$dateToParts: {date: "$date", timezone: tzExpr}}}},
        {$_internalSplitPipeline: {mergeType: "mongos"}},
        {
            $addFields: {
                mongosDate: {
                    $dateFromParts: {
                        year: "$mongodParts.year",
                        month: "$mongodParts.month",
                        day: "$mongodParts.day",
                        hour: "$mongodParts.hour",
                        minute: "$mongodParts.minute",
                        second: "$mongodParts.second",
                        millisecond: "$mongodParts.millisecond",
                        timezone: tzExpr
                    }
                }
            }
        },
        {$match: {$expr: {$eq: ["$date", "$mongosDate"]}}}
    ];
}

// Confirm that the pipe splits at '$_internalSplitPipeline' and that the merge runs on mongoS.
let timeZonePipeline = buildTimeZonePipeline("GMT");
const tzExplain = assert.commandWorked(mongosColl.explain().aggregate(timeZonePipeline));
assert.eq(tzExplain.splitPipeline.shardsPart, [timeZonePipeline[0]]);
assert.eq(tzExplain.splitPipeline.mergerPart, timeZonePipeline.slice(1));
assert.eq(tzExplain.mergeType, "mongos");

// Confirm that both documents are output by the pipeline, demonstrating that the date has been
// correctly disassembled on mongoD and reassembled on mongoS.
assert.eq(mongosColl.aggregate(timeZonePipeline).itcount(), 2);

// Confirm that aggregating with a timezone which is not present in 'good_timezone_info' fails.
timeZonePipeline = buildTimeZonePipeline("Europe/Dublin");
assert.eq(assert.throws(() => mongosColl.aggregate(timeZonePipeline)).code, 40485);

st.stop();
})();