diff options
author | Anton Korshunov <anton.korshunov@mongodb.com> | 2021-06-01 12:22:25 +0100 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-06-03 08:02:19 +0000 |
commit | 1aa41b5625ae0ac2e757f3638585e20d7d81e7ca (patch) | |
tree | 76ca00b164415dc1d3d1243179906bfa50255808 /jstests/noPassthrough/oplog_scan_optimizations_do_not_conflict.js | |
parent | dcd9f60e591acffeedabb5fa368f407a035473b2 (diff) | |
download | mongo-1aa41b5625ae0ac2e757f3638585e20d7d81e7ca.tar.gz |
SERVER-57317 Fix optimized oplog scans in SBE to correctly handle $_resumeAfter queries
Diffstat (limited to 'jstests/noPassthrough/oplog_scan_optimizations_do_not_conflict.js')
-rw-r--r-- | jstests/noPassthrough/oplog_scan_optimizations_do_not_conflict.js | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/jstests/noPassthrough/oplog_scan_optimizations_do_not_conflict.js b/jstests/noPassthrough/oplog_scan_optimizations_do_not_conflict.js new file mode 100644 index 00000000000..e62fa2ff094 --- /dev/null +++ b/jstests/noPassthrough/oplog_scan_optimizations_do_not_conflict.js @@ -0,0 +1,152 @@ +/** + * Test that optimized collection scan on the oplog collection can be applied and executed + * successfully. + * @tags: [ + * requires_replication, + * ] + */ +(function() { +"use strict"; + +const rst = new ReplSetTest({nodes: 2}); +rst.startSet(); +rst.initiate(); + +const db = rst.getPrimary().getDB("oplog_scan_optimizations"); +const localDb = rst.getPrimary().getDB("local"); +const collName = "oplog_scan_optimizations"; +const oplogCollName = "oplog.rs"; +const coll = db[collName]; +const oplogColl = localDb[oplogCollName]; + +coll.drop(); + +// Insert several document so that we can use a cursor to fetch them in multiple batches. +const testData = [{_id: 0, a: 1}, {_id: 1, a: 2}, {_id: 2, a: 3}]; +assert.commandWorked(coll.insert(testData)); + +// Run the initial query and request to return a resume token. We will also request to return a +// recordId for the document as it will +// be used as a $_resumeAfter token later. +let res = assert.commandWorked(localDb.runCommand({ + find: oplogCollName, + filter: {op: "i", "o.a": {$gte: 1}}, + hint: {$natural: 1}, + batchSize: 1, + showRecordId: true, + $_requestResumeToken: true +})); +assert.eq(1, res.cursor.firstBatch.length); +assert(res.cursor.firstBatch[0].hasOwnProperty("$recordId")); +assert.hasFields(res.cursor, ["postBatchResumeToken"]); + +const firstResumeToken = res.cursor.postBatchResumeToken; +const firstOplogBatch = res.cursor.firstBatch; + +res = assert.commandWorked( + localDb.runCommand({getMore: res.cursor.id, collection: oplogCollName, batchSize: 1})); +assert.eq(1, res.cursor.nextBatch.length); +assert(res.cursor.nextBatch[0].hasOwnProperty("$recordId")); +assert.hasFields(res.cursor, ["postBatchResumeToken"]); + +const secondResumeToken = res.cursor.postBatchResumeToken; +const secondOplogBatch = res.cursor.nextBatch; + +// Kill the cursor before attempting to resume. +assert.commandWorked(localDb.runCommand({killCursors: oplogCollName, cursors: [res.cursor.id]})); + +// Try to resume the query from the saved resume token. This fails because '$_resumeAfter' expects a +// '$recordId' resume token, not a 'ts' resume token. This is appropriate since resuming on the +// oplog should occur by timestamp, not by record id. +assert.commandFailedWithCode(localDb.runCommand({ + find: oplogCollName, + filter: {ts: {$gte: firstResumeToken.ts}}, + hint: {$natural: 1}, + batchSize: 1, + $_requestResumeToken: true, + $_resumeAfter: firstResumeToken +}), + ErrorCodes.BadValue); + +// Now try to resume using a '$recordId' resume token, which doesn't make a ton of sense, but is +// still allowed. +res = assert.commandWorked(localDb.runCommand({ + find: oplogCollName, + filter: {ts: {$gte: firstResumeToken.ts}}, + hint: {$natural: 1}, + batchSize: 1, + showRecordId: true, + $_requestResumeToken: true, + $_resumeAfter: {$recordId: firstOplogBatch[0].$recordId} +})); +assert.eq(1, res.cursor.firstBatch.length); +assert.eq(secondOplogBatch[0], res.cursor.firstBatch[0]); + +// Kill the cursor before attempting to resume. +assert.commandWorked(localDb.runCommand({killCursors: oplogCollName, cursors: [res.cursor.id]})); + +res = assert.commandWorked(localDb.runCommand({ + find: oplogCollName, + filter: {ts: {$lte: secondResumeToken.ts}}, + hint: {$natural: 1}, + batchSize: 1, + showRecordId: true, + $_requestResumeToken: true, + $_resumeAfter: {$recordId: firstOplogBatch[0].$recordId} +})); +assert.eq(1, res.cursor.firstBatch.length); +assert.eq(secondOplogBatch[0], res.cursor.firstBatch[0]); + +// Kill the cursor before attempting to resume. +assert.commandWorked(localDb.runCommand({killCursors: oplogCollName, cursors: [res.cursor.id]})); + +res = assert.commandWorked(localDb.runCommand({ + find: oplogCollName, + filter: {ts: {$eq: secondResumeToken.ts}}, + hint: {$natural: 1}, + batchSize: 1, + showRecordId: true, + $_requestResumeToken: true, + $_resumeAfter: {$recordId: firstOplogBatch[0].$recordId} +})); +assert.eq(1, res.cursor.firstBatch.length); +assert.eq(secondOplogBatch[0], res.cursor.firstBatch[0]); + +// Kill the cursor before attempting to resume. +assert.commandWorked(localDb.runCommand({killCursors: oplogCollName, cursors: [res.cursor.id]})); + +res = assert.commandWorked(localDb.runCommand({ + find: oplogCollName, + filter: {ts: {$gt: firstResumeToken.ts, $lte: secondResumeToken.ts}}, + hint: {$natural: 1}, + batchSize: 1, + showRecordId: true, + $_requestResumeToken: true, + $_resumeAfter: {$recordId: firstOplogBatch[0].$recordId} +})); +assert.eq(1, res.cursor.firstBatch.length); +assert.eq(secondOplogBatch[0], res.cursor.firstBatch[0]); + +// Kill the cursor before attempting to resume. +assert.commandWorked(localDb.runCommand({killCursors: oplogCollName, cursors: [res.cursor.id]})); + +// Try to resume the query from a non-existent recordId and check that it fails to position the +// cursor to the record specified in the resume token. +assert.commandFailedWithCode(localDb.runCommand({ + find: oplogCollName, + hint: {$natural: 1}, + batchSize: 1, + $_requestResumeToken: true, + $_resumeAfter: {$recordId: NumberLong("50")} +}), + ErrorCodes.KeyNotFound); + +// When we have a predicate on a 'ts' field of the oplog collection, we can build an optimized +// collection scan. Make sure we can run such optimized scans and get the result. +assert.eq(oplogColl.find({ts: {$gte: firstResumeToken.ts}}).itcount(), 3); +assert.eq(oplogColl.find({ts: {$gt: firstResumeToken.ts}}).itcount(), 2); +assert.gt(oplogColl.find({ts: {$lte: firstResumeToken.ts}}).itcount(), 1); +assert.gt(oplogColl.find({ts: {$lt: firstResumeToken.ts}}).itcount(), 1); + +rst.stopSet(); +})(); |