summaryrefslogtreecommitdiff
path: root/jstests/replsets/oplog_format.js
blob: a2c37d294a5a17bc37d819e294080c1b0fb20c9b (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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/**
 * These tests verify that the oplog entries are created correctly for updates
 *
 * Do not add more tests here but instead add C++ unit tests in db/ops/modifier*_test files
 *
 */

"use strict";
var replTest = new ReplSetTest({nodes: 1, oplogSize: 2, nodeOptions: {smallfiles: ""}});
var nodes = replTest.startSet();
replTest.initiate();
var master = replTest.getPrimary();
var coll = master.getDB("o").fake;
var cdb = coll.getDB();

var assertLastOplog = function(o, o2, msg) {
    var last = master.getDB("local").oplog.rs.find().limit(1).sort({$natural: -1}).next();

    assert.eq(last.ns, coll.getFullName(), "ns bad : " + msg);
    assert.docEq(last.o, o, "o bad : " + msg);
    if (o2)
        assert.docEq(last.o2, o2, "o2 bad : " + msg);
    return last.ts;
};

// set things up.
coll.save({_id: 1});
assertLastOplog({_id: 1}, null, "save -- setup ");

/**
 * The first ones are from the old updatetests which tested the internal impl using a modSet
 */

var msg = "IncRewriteExistingField: $inc $set";
coll.save({_id: 1, a: 2});
assertLastOplog({_id: 1, a: 2}, {_id: 1}, "save " + msg);
var res = assert.writeOK(coll.update({}, {$inc: {a: 1}, $set: {b: 2}}));
assert.eq(res.nModified, 1, "update failed for '" + msg + "': " + res.toString());
assert.docEq({_id: 1, a: 3, b: 2}, coll.findOne({}), msg);
assertLastOplog({$set: {a: 3, b: 2}}, {_id: 1}, msg);

var msg = "IncRewriteNonExistingField: $inc $set";
coll.save({_id: 1, c: 0});
assertLastOplog({_id: 1, c: 0}, {_id: 1}, "save " + msg);
res = assert.writeOK(coll.update({}, {$inc: {a: 1}, $set: {b: 2}}));
assert.eq(res.nModified, 1, "update failed for '" + msg + "': " + res.toString());
assert.docEq({_id: 1, c: 0, a: 1, b: 2}, coll.findOne({}), msg);
assertLastOplog({$set: {a: 1, b: 2}}, {_id: 1}, msg);

var msg = "TwoNestedPulls: two $pull";
coll.save({_id: 1, a: {b: [1, 2], c: [1, 2]}});
assertLastOplog({_id: 1, a: {b: [1, 2], c: [1, 2]}}, {_id: 1}, "save " + msg);
res = assert.writeOK(coll.update({}, {$pull: {'a.b': 2, 'a.c': 2}}));
assert.eq(res.nModified, 1, "update failed for '" + msg + "': " + res.toString());
assert.docEq({_id: 1, a: {b: [1], c: [1]}}, coll.findOne({}), msg);
assertLastOplog({$set: {'a.b': [1], 'a.c': [1]}}, {_id: 1}, msg);

var msg = "MultiSets: two $set";
coll.save({_id: 1, a: 1, b: 1});
assertLastOplog({_id: 1, a: 1, b: 1}, {_id: 1}, "save " + msg);
res = assert.writeOK(coll.update({}, {$set: {a: 2, b: 2}}));
assert.eq(res.nModified, 1, "update failed for '" + msg + "': " + res.toString());
assert.docEq({_id: 1, a: 2, b: 2}, coll.findOne({}), msg);
assertLastOplog({$set: {a: 2, b: 2}}, {_id: 1}, msg);

// More tests to validate the oplog format and correct excution

var msg = "bad single $set";
coll.save({_id: 1, a: 1});
assertLastOplog({_id: 1, a: 1}, {_id: 1}, "save " + msg);
res = assert.writeOK(coll.update({}, {$set: {a: 2}}));
assert.eq(res.nModified, 1, "update failed for '" + msg + "': " + res.toString());
assert.docEq({_id: 1, a: 2}, coll.findOne({}), msg);
assertLastOplog({$set: {a: 2}}, {_id: 1}, msg);

var msg = "bad single $inc";
res = assert.writeOK(coll.update({}, {$inc: {a: 1}}));
assert.eq(res.nModified, 1, "update failed for '" + msg + "': " + res.toString());
assert.docEq({_id: 1, a: 3}, coll.findOne({}), msg);
assertLastOplog({$set: {a: 3}}, {_id: 1}, msg);

var msg = "bad double $set";
res = assert.writeOK(coll.update({}, {$set: {a: 2, b: 2}}));
assert.eq(res.nModified, 1, "update failed for '" + msg + "': " + res.toString());
assert.docEq({_id: 1, a: 2, b: 2}, coll.findOne({}), msg);
assertLastOplog({$set: {a: 2, b: 2}}, {_id: 1}, msg);

var msg = "bad save";
assert.writeOK(coll.save({_id: 1, a: [2]}));
assert.docEq({_id: 1, a: [2]}, coll.findOne({}), msg);
assertLastOplog({_id: 1, a: [2]}, {_id: 1}, msg);

var msg = "bad array $inc";
res = assert.writeOK(coll.update({}, {$inc: {"a.0": 1}}));
assert.eq(res.nModified, 1, "update failed for '" + msg + "': " + res.toString());
assert.docEq({_id: 1, a: [3]}, coll.findOne({}), msg);
var lastTS = assertLastOplog({$set: {"a.0": 3}}, {_id: 1}, msg);

var msg = "bad $setOnInsert";
res = assert.writeOK(coll.update({}, {$setOnInsert: {a: -1}}));
assert.eq(res.nMatched, 1, "update failed for '" + msg + "': " + res.toString());
assert.docEq({_id: 1, a: [3]}, coll.findOne({}), msg);               // No-op
var otherTS = assertLastOplog({$set: {"a.0": 3}}, {_id: 1}, msg);    // Nothing new
assert.eq(lastTS, otherTS, "new oplog was not expected -- " + msg);  // No new oplog entry

coll.remove({});
assert.eq(coll.count(), 0, "collection not empty");

var msg = "bad $setOnInsert w/upsert";
res = assert.writeOK(coll.update({}, {$setOnInsert: {a: 200}}, {upsert: true}));  // upsert
assert.eq(res.nUpserted, 1, "update failed for '" + msg + "': " + res.toString());
var id = res.getUpsertedId()._id;
assert.docEq({_id: id, a: 200}, coll.findOne({}), msg);  // No-op
assertLastOplog({_id: id, a: 200}, null, msg);           // No new oplog entry

coll.remove({});
assert.eq(coll.count(), 0, "collection not empty-2");

/* inconsistent oplog format with old code -- new is okay but less efficient
 * enable once we switch the default
var msg = "bad array $push";
coll.save({_id:1, a:[1,2]})
coll.update({}, {$push:{a:3}});
var gle = cdb.getLastErrorObj();
assert.isnull(gle.err, msg);
assert.eq(gle.n, 1, "update failed for '" + msg +"': "+ tojson(gle));
assert.docEq({_id:1, a:[1,2,3]}, coll.findOne({}), msg);
//assertLastOplog({$set:{"a.2": 3}}, {_id:1}, msg); // old format
assertLastOplog({$set:{"a": [1,2,3]}}, {_id:1}, msg); // new format
 */

var msg = "bad array $push 2";
coll.save({_id: 1, a: "foo"});
res = assert.writeOK(coll.update({}, {$push: {c: 18}}));
assert.eq(res.nModified, 1, "update failed for '" + msg + "': " + res.toString());
assert.docEq({_id: 1, a: "foo", c: [18]}, coll.findOne({}), msg);
assertLastOplog({$set: {"c": [18]}}, {_id: 1}, msg);

var msg = "bad array $push $slice";
coll.save({_id: 1, a: {b: [18]}});
res = assert.writeOK(coll.update({_id: {$gt: 0}}, {$push: {"a.b": {$each: [1, 2], $slice: -2}}}));
assert.eq(res.nModified, 1, "update failed for '" + msg + "': " + res.toString());
assert.docEq({_id: 1, a: {b: [1, 2]}}, coll.findOne({}), msg);
assertLastOplog({$set: {"a.b": [1, 2]}}, {_id: 1}, msg);

var msg = "bad array $push $sort ($slice -100)";
coll.save({_id: 1, a: {b: [{c: 2}, {c: 1}]}});
res = assert.writeOK(
    coll.update({}, {$push: {"a.b": {$each: [{c: -1}], $sort: {c: 1}, $slice: -100}}}));
assert.eq(res.nModified, 1, "update failed for '" + msg + "': " + res.toString());
assert.docEq({_id: 1, a: {b: [{c: -1}, {c: 1}, {c: 2}]}}, coll.findOne({}), msg);
assertLastOplog({$set: {"a.b": [{c: -1}, {c: 1}, {c: 2}]}}, {_id: 1}, msg);

var msg = "bad array $push $slice $sort";
coll.save({_id: 1, a: [{b: 2}, {b: 1}]});
res = assert.writeOK(
    coll.update({_id: {$gt: 0}}, {$push: {a: {$each: [{b: -1}], $slice: -2, $sort: {b: 1}}}}));
assert.eq(res.nModified, 1, "update failed for '" + msg + "': " + res.toString());
assert.docEq({_id: 1, a: [{b: 1}, {b: 2}]}, coll.findOne({}), msg);
assertLastOplog({$set: {a: [{b: 1}, {b: 2}]}}, {_id: 1}, msg);

var msg = "bad array $push $slice $sort first two";
coll.save({_id: 1, a: {b: [{c: 2}, {c: 1}]}});
res = assert.writeOK(
    coll.update({_id: {$gt: 0}}, {$push: {"a.b": {$each: [{c: -1}], $slice: -2, $sort: {c: 1}}}}));
assert.eq(res.nModified, 1, "update failed for '" + msg + "': " + res.toString());
assert.docEq({_id: 1, a: {b: [{c: 1}, {c: 2}]}}, coll.findOne({}), msg);
assertLastOplog({$set: {"a.b": [{c: 1}, {c: 2}]}}, {_id: 1}, msg);

var msg = "bad array $push $slice $sort reversed first two";
coll.save({_id: 1, a: {b: [{c: 1}, {c: 2}]}});
res = assert.writeOK(
    coll.update({_id: {$gt: 0}}, {$push: {"a.b": {$each: [{c: -1}], $slice: -2, $sort: {c: -1}}}}));
assert.eq(res.nModified, 1, "update failed for '" + msg + "': " + res.toString());
assert.docEq({_id: 1, a: {b: [{c: 1}, {c: -1}]}}, coll.findOne({}), msg);
assertLastOplog({$set: {"a.b": [{c: 1}, {c: -1}]}}, {_id: 1}, msg);

replTest.stopSet();