|
4 | 4 | (function() { |
5 | 5 | "use strict"; |
6 | 6 |
|
7 | | - var oplogProjection = {$project: {"_id.ts": 0}}; |
8 | | - |
9 | 7 | // Helper for testing that pipeline returns correct set of results. |
10 | 8 | function testPipeline(pipeline, expectedResult, collection) { |
11 | | - // Limit to the last N documents from the end of the oplog, because currently |
12 | | - // $changeNotification always comes from the start of the oplog. |
13 | | - pipeline.push({$sort: {"_id.ts": -1}}); |
14 | | - if (expectedResult.length > 0) { |
15 | | - pipeline.push({$limit: expectedResult.length}); |
16 | | - } |
17 | 9 | // Strip the oplog fields we aren't testing. |
18 | | - pipeline.push(oplogProjection); |
19 | | - assert.docEq(collection.aggregate(pipeline).toArray().reverse(), expectedResult); |
| 10 | + pipeline.push({$limit: 1}); |
| 11 | + pipeline.push({$project: {"_id.ts": 0}}); |
| 12 | + assert.docEq(collection.aggregate(pipeline).toArray(), expectedResult); |
20 | 13 | } |
21 | 14 |
|
22 | | - let replTest = new ReplSetTest({name: 'changeNotificationTest', nodes: 1}); |
23 | | - let nodes = replTest.startSet(); |
| 15 | + var replTest = new ReplSetTest({name: 'changeNotificationTest', nodes: 1}); |
| 16 | + var nodes = replTest.startSet(); |
24 | 17 | replTest.initiate(); |
25 | 18 | replTest.awaitReplication(); |
26 | 19 |
|
27 | 20 | db = replTest.getPrimary().getDB('test'); |
28 | | - db.getMongo().forceReadMode('commands'); |
29 | 21 |
|
30 | 22 | jsTestLog("Testing single insert"); |
31 | 23 | assert.writeOK(db.t1.insert({_id: 0, a: 1})); |
|
137 | 129 | assert.writeOK(db.t3.insert({_id: 101, renameCollection: "test.dne1", to: "test.dne2"})); |
138 | 130 | testPipeline([{$changeNotification: {}}], [], db.dne1); |
139 | 131 | testPipeline([{$changeNotification: {}}], [], db.dne2); |
140 | | - |
141 | | - // Now make sure the cursor behaves like a tailable awaitData cursor. |
142 | | - jsTestLog("Testing tailability"); |
143 | | - let tailableCursor = db.tailable1.aggregate([{$changeNotification: {}}, oplogProjection]); |
144 | | - assert(!tailableCursor.hasNext()); |
145 | | - assert.writeOK(db.tailable1.insert({_id: 101, a: 1})); |
146 | | - assert(tailableCursor.hasNext()); |
147 | | - assert.docEq(tailableCursor.next(), { |
148 | | - "_id": { |
149 | | - "_id": 101, |
150 | | - "ns": "test.tailable1", |
151 | | - }, |
152 | | - "documentKey": {"_id": 101}, |
153 | | - "newDocument": {"_id": 101, "a": 1}, |
154 | | - "ns": {"coll": "tailable1", "db": "test"}, |
155 | | - "operationType": "insert" |
156 | | - }); |
157 | | - |
158 | | - jsTestLog("Testing awaitdata"); |
159 | | - let res = assert.commandWorked(db.runCommand({ |
160 | | - aggregate: "tailable2", |
161 | | - pipeline: [{$changeNotification: {}}, oplogProjection], |
162 | | - cursor: {} |
163 | | - })); |
164 | | - let aggcursor = res.cursor; |
165 | | - |
166 | | - // We should get a valid cursor. |
167 | | - assert.neq(aggcursor.id, 0); |
168 | | - |
169 | | - // Initial batch size should be zero as there should be no data. |
170 | | - assert.eq(aggcursor.firstBatch.length, 0); |
171 | | - |
172 | | - // No data, so should return no results, but cursor should remain valid. |
173 | | - res = assert.commandWorked( |
174 | | - db.runCommand({getMore: aggcursor.id, collection: "tailable2", maxTimeMS: 50})); |
175 | | - aggcursor = res.cursor; |
176 | | - assert.neq(aggcursor.id, 0); |
177 | | - assert.eq(aggcursor.nextBatch.length, 0); |
178 | | - |
179 | | - // Now insert something in parallel while waiting for it. |
180 | | - let insertshell = startParallelShell(function() { |
181 | | - // Wait for the getMore to appear in currentop. |
182 | | - assert.soon(function() { |
183 | | - return db.currentOp({op: "getmore", "command.collection": "tailable2"}).inprog.length == |
184 | | - 1; |
185 | | - }); |
186 | | - assert.writeOK(db.tailable2.insert({_id: 102, a: 2})); |
187 | | - }); |
188 | | - res = assert.commandWorked( |
189 | | - db.runCommand({getMore: aggcursor.id, collection: "tailable2", maxTimeMS: 5 * 60 * 1000})); |
190 | | - aggcursor = res.cursor; |
191 | | - assert.eq(aggcursor.nextBatch.length, 1); |
192 | | - assert.docEq(aggcursor.nextBatch[0], { |
193 | | - "_id": { |
194 | | - "_id": 102, |
195 | | - "ns": "test.tailable2", |
196 | | - }, |
197 | | - "documentKey": {"_id": 102}, |
198 | | - "newDocument": {"_id": 102, "a": 2}, |
199 | | - "ns": {"coll": "tailable2", "db": "test"}, |
200 | | - "operationType": "insert" |
201 | | - }); |
202 | | - |
203 | | - // Wait for insert shell to terminate. |
204 | | - insertshell(); |
205 | | - |
206 | | - jsTestLog("Testing awaitdata - no wake on insert to another collection"); |
207 | | - res = assert.commandWorked(db.runCommand({ |
208 | | - aggregate: "tailable3", |
209 | | - pipeline: [{$changeNotification: {}}, oplogProjection], |
210 | | - cursor: {} |
211 | | - })); |
212 | | - aggcursor = res.cursor; |
213 | | - // We should get a valid cursor. |
214 | | - assert.neq(aggcursor.id, 0); |
215 | | - |
216 | | - // Initial batch size should be zero as there should be no data. |
217 | | - assert.eq(aggcursor.firstBatch.length, 0); |
218 | | - |
219 | | - // Now insert something in a different collection in parallel while waiting. |
220 | | - insertshell = startParallelShell(function() { |
221 | | - // Wait for the getMore to appear in currentop. |
222 | | - assert.soon(function() { |
223 | | - return db.currentOp({op: "getmore", "command.collection": "tailable3"}).inprog.length == |
224 | | - 1; |
225 | | - }); |
226 | | - assert.writeOK(db.tailable3a.insert({_id: 103, a: 2})); |
227 | | - }); |
228 | | - let start = new Date(); |
229 | | - res = assert.commandWorked( |
230 | | - db.runCommand({getMore: aggcursor.id, collection: "tailable3", maxTimeMS: 1000})); |
231 | | - let diff = (new Date()).getTime() - start.getTime(); |
232 | | - assert.gt(diff, 900, "AwaitData returned prematurely on insert to unrelated collection."); |
233 | | - aggcursor = res.cursor; |
234 | | - // Cursor should be valid with no data. |
235 | | - assert.neq(aggcursor.id, 0); |
236 | | - assert.eq(aggcursor.nextBatch.length, 0); |
237 | | - |
238 | | - // Wait for insert shell to terminate. |
239 | | - insertshell(); |
240 | | - |
241 | | - // This time, put something in a different collection, then in the correct collection. |
242 | | - // We should wake up with just the correct data. |
243 | | - insertshell = startParallelShell(function() { |
244 | | - // Wait for the getMore to appear in currentop. |
245 | | - assert.soon(function() { |
246 | | - return db.currentOp({op: "getmore", "command.collection": "tailable3"}).inprog.length == |
247 | | - 1; |
248 | | - }); |
249 | | - assert.writeOK(db.tailable3a.insert({_id: 104, a: 2})); |
250 | | - assert(db.currentOp({op: "getmore", "command.collection": "tailable3"}).inprog.length == 1); |
251 | | - assert.writeOK(db.tailable3.insert({_id: 105, a: 3})); |
252 | | - }); |
253 | | - res = assert.commandWorked( |
254 | | - db.runCommand({getMore: aggcursor.id, collection: "tailable3", maxTimeMS: 5 * 60 * 1000})); |
255 | | - aggcursor = res.cursor; |
256 | | - assert.neq(aggcursor.id, 0); |
257 | | - assert.eq(aggcursor.nextBatch.length, 1); |
258 | | - assert.docEq(aggcursor.nextBatch[0], { |
259 | | - "_id": { |
260 | | - "_id": 105, |
261 | | - "ns": "test.tailable3", |
262 | | - }, |
263 | | - "documentKey": {"_id": 105}, |
264 | | - "newDocument": {"_id": 105, "a": 3}, |
265 | | - "ns": {"coll": "tailable3", "db": "test"}, |
266 | | - "operationType": "insert" |
267 | | - }); |
268 | | - |
269 | | - // Wait for insert shell to terminate. |
270 | | - insertshell(); |
271 | | - |
272 | | - jsTestLog("Ensuring attempt to read with legacy operations fails."); |
273 | | - db.getMongo().forceReadMode('legacy'); |
274 | | - tailableCursor = db.tailable2.aggregate([{$changeNotification: {}}, oplogProjection], |
275 | | - {cursor: {batchSize: 0}}); |
276 | | - assert.throws(function() { |
277 | | - tailableCursor.next(); |
278 | | - }, [], "Legacy getMore expected to fail on changeNotification cursor."); |
279 | | - |
280 | 132 | replTest.stopSet(); |
281 | 133 | }()); |
0 commit comments