diff options
Diffstat (limited to 'jstests')
-rw-r--r-- | jstests/auth/lib/commands_lib.js | 21 | ||||
-rw-r--r-- | jstests/core/views/views_all_commands.js | 1 | ||||
-rw-r--r-- | jstests/noPassthrough/shardsvr_global_index_crud_bulk.js | 649 | ||||
-rw-r--r-- | jstests/replsets/all_commands_downgrading_to_upgraded.js | 1 | ||||
-rw-r--r-- | jstests/replsets/db_reads_while_recovering_all_commands.js | 1 | ||||
-rw-r--r-- | jstests/replsets/global_index_ddl_rollback.js (renamed from jstests/replsets/shardsvr_global_index_ddl_rollback.js) | 0 | ||||
-rw-r--r-- | jstests/replsets/shardsvr_global_index_crud_rollback.js | 1 | ||||
-rw-r--r-- | jstests/sharding/read_write_concern_defaults_application.js | 1 |
8 files changed, 675 insertions, 0 deletions
diff --git a/jstests/auth/lib/commands_lib.js b/jstests/auth/lib/commands_lib.js index 12f2774c306..f868ecd612f 100644 --- a/jstests/auth/lib/commands_lib.js +++ b/jstests/auth/lib/commands_lib.js @@ -2839,6 +2839,27 @@ var authCommandsLib = { ] }, { + testname: "_shardsvrWriteGlobalIndexKeys", + command: {_shardsvrWriteGlobalIndexKeys: 1, ops: [ + {_shardsvrInsertGlobalIndexKey: UUID(), key: {a: 1}, docKey: {shk: 1, _id: 1}}, + {_shardsvrDeleteGlobalIndexKey: UUID(), key: {a: 1}, docKey: {shk: 1, _id: 1}}, + ]}, + skipSharded: true, + testcases: [ + { + runOnDb: adminDbName, + roles: {__system: 1}, + privileges: [{resource: {cluster: true}, actions: ["internal"]}], + expectFail: true + }, + {runOnDb: firstDbName, + roles: {__system: 1}, + privileges: [{resource: {cluster: true}, actions: ["internal"]}], + expectFail: true + }, + ] + }, + { testname: "commitTxn", command: {commitTransaction: 1}, // TODO (SERVER-53497): Enable auth testing for abortTransaction and commitTransaction. diff --git a/jstests/core/views/views_all_commands.js b/jstests/core/views/views_all_commands.js index 0f7b6e04869..63a1d8242c4 100644 --- a/jstests/core/views/views_all_commands.js +++ b/jstests/core/views/views_all_commands.js @@ -150,6 +150,7 @@ let viewsCommandTests = { _shardsvrDropIndexes: {skip: isAnInternalCommand}, _shardsvrInsertGlobalIndexKey: {skip: isAnInternalCommand}, _shardsvrDeleteGlobalIndexKey: {skip: isAnInternalCommand}, + _shardsvrWriteGlobalIndexKeys: {skip: isAnInternalCommand}, _shardsvrCleanupReshardCollection: {skip: isAnInternalCommand}, _shardsvrRegisterIndex: {skip: isAnInternalCommand}, _shardsvrCommitIndexParticipant: {skip: isAnInternalCommand}, diff --git a/jstests/noPassthrough/shardsvr_global_index_crud_bulk.js b/jstests/noPassthrough/shardsvr_global_index_crud_bulk.js new file mode 100644 index 00000000000..2d5976d777b --- /dev/null +++ b/jstests/noPassthrough/shardsvr_global_index_crud_bulk.js @@ -0,0 +1,649 @@ +/** + * Tests _shardsvrWriteGlobalIndexKeys, which is sometimes referred to as 'bulk write' as it runs + * multiple _shardsvrInsertGlobalIndexKey and _shardsvrDeleteGlobalIndexKey statements in bulk. + * + * @tags: [ + * featureFlagGlobalIndexes, + * requires_fcv_62, + * requires_replication, + * ] + */ + +(function() { +"use strict"; + +function uuidToString(uuid) { + const [_, uuidString] = uuid.toString().match(/"((?:\\.|[^"\\])*)"/); + return uuidString; +} + +function entriesInContainer(primary, uuid) { + return primary.getDB("system") + .getCollection("globalIndexes." + uuidToString(uuid)) + .find() + .itcount(); +} + +// Speed up test time. +const maxNumberOfTxnOpsInSingleOplogEntry = 1000; + +const rst = new ReplSetTest({ + nodes: 2, + nodeOptions: { + setParameter: { + maxNumberOfTransactionOperationsInSingleOplogEntry: maxNumberOfTxnOpsInSingleOplogEntry + } + } +}); +rst.startSet(); +rst.initiate(); +const primary = rst.getPrimary(); +const adminDB = primary.getDB("admin"); +const globalIndexUUID = UUID(); +const session = primary.startSession(); + +assert.commandWorked(adminDB.runCommand({_shardsvrCreateGlobalIndex: globalIndexUUID})); +assert.eq(0, entriesInContainer(primary, globalIndexUUID)); + +// _shardsvrWriteGlobalIndexKeys must run inside a transaction. +assert.commandFailedWithCode(adminDB.runCommand({_shardsvrWriteGlobalIndexKeys: 1, ops: []}), + 6789500); + +// Missing required '_shardsvrWriteGlobalIndexKeys.ops' field. +{ + session.startTransaction(); + assert.commandFailedWithCode( + session.getDatabase("test").runCommand({_shardsvrWriteGlobalIndexKeys: 1}), 40414); + session.abortTransaction(); +} + +// _shardsvrWriteGlobalIndexKeys requires at least one statement in the 'ops' array. +{ + const containerEntriesBefore = entriesInContainer(primary, globalIndexUUID); + + session.startTransaction(); + assert.commandFailedWithCode( + session.getDatabase("test").runCommand({_shardsvrWriteGlobalIndexKeys: 1, ops: []}), + 6789502); + session.abortTransaction(); + const containerEntriesAfter = entriesInContainer(primary, globalIndexUUID); + + assert.eq(containerEntriesAfter, containerEntriesBefore); +} + +// Only _shardsvrInsertGlobalIndexKey and _shardsvrDeleteGlobalIndexKey commands are allowed. +{ + session.startTransaction(); + assert.commandFailedWithCode(session.getDatabase("test").runCommand( + {_shardsvrWriteGlobalIndexKeys: 1, ops: [{otherCommand: 1}]}), + 6789501); + session.abortTransaction(); +} + +// Invalid UUID in _shardsvrWriteGlobalIndexKeys statement. +{ + session.startTransaction(); + assert.commandFailedWithCode(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [{ + "_shardsvrInsertGlobalIndexKey": "not_a_uuid", + key: {a: "hola"}, + docKey: {sk: 3, _id: 3} + }] + }), + ErrorCodes.InvalidUUID); + session.abortTransaction(); +} + +// Unexisting UUID in statement. +{ + session.startTransaction(); + assert.commandFailedWithCode(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [{"_shardsvrInsertGlobalIndexKey": UUID(), key: {a: "hola"}, docKey: {sk: 3, _id: 3}}] + }), + 6789402); + session.abortTransaction(); +} + +// Missing 'key' field in statement. +{ + session.startTransaction(); + assert.commandFailedWithCode(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [{"_shardsvrInsertGlobalIndexKey": globalIndexUUID, docKey: {sk: 3, _id: 3}}] + }), + 40414); + session.abortTransaction(); +} + +// Missing 'docKey' field in statement. +{ + session.startTransaction(); + assert.commandFailedWithCode(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [{"_shardsvrInsertGlobalIndexKey": globalIndexUUID, key: {a: "hola"}}] + }), + 40414); + session.abortTransaction(); +} + +// Bulk insert a single entry, then bulk delete it. +{ + const containerEntriesBefore = entriesInContainer(primary, globalIndexUUID); + + session.startTransaction(); + assert.commandWorked(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [{ + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {myKey: "abc"}, + docKey: {s: 123, _id: 987} + }] + })); + session.commitTransaction(); + + const containerEntriesAfterInsert = entriesInContainer(primary, globalIndexUUID); + assert.eq(containerEntriesAfterInsert, containerEntriesBefore + 1); + + session.startTransaction(); + assert.commandWorked(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [{ + _shardsvrDeleteGlobalIndexKey: globalIndexUUID, + key: {myKey: "abc"}, + docKey: {s: 123, _id: 987} + }] + })); + session.commitTransaction(); + + const containerEntriesAfterDelete = entriesInContainer(primary, globalIndexUUID); + assert.eq(containerEntriesAfterDelete, containerEntriesBefore); +} + +// Update an index key. +{ + const containerEntriesBefore = entriesInContainer(primary, globalIndexUUID); + + session.startTransaction(); + assert.commandWorked(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [{ + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {myKey: "the quick brown fox"}, + docKey: {myShardKey0: "jumped over", myShardKey1: "the lazy dog", _id: 1234567890} + }] + })); + session.commitTransaction(); + + const containerEntriesAfterInsert = entriesInContainer(primary, globalIndexUUID); + assert.eq(containerEntriesAfterInsert, containerEntriesBefore + 1); + + // We can't reinsert that same key, as that'll trigger a duplicate key error. + session.startTransaction(); + assert.commandFailedWithCode(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [{ + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {myKey: "the quick brown fox"}, + docKey: {myShardKey0: "another", myShardKey1: "value", _id: 0} + }] + }), + ErrorCodes.DuplicateKey); + session.abortTransaction(); + + // Update the key. + session.startTransaction(); + assert.commandWorked(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [ + { + _shardsvrDeleteGlobalIndexKey: globalIndexUUID, + key: {myKey: "the quick brown fox"}, + docKey: {myShardKey0: "jumped over", myShardKey1: "the lazy dog", _id: 1234567890} + }, + { + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {myKey: "The Quick Brown Fox"}, + docKey: {myShardKey0: "jumped over", myShardKey1: "the lazy dog", _id: 1234567890} + } + ] + })); + session.commitTransaction(); + + const containerEntriesAfterUpdate = entriesInContainer(primary, globalIndexUUID); + assert.eq(containerEntriesAfterInsert, containerEntriesAfterUpdate); + + // Verify that the index key update succeeded: we can re-insert the old key, we get a duplicate + // key error if reinsterting the updated key. + session.startTransaction(); + assert.commandWorked(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [ + { + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {myKey: "the quick brown fox"}, + docKey: {myShardKey0: "another", myShardKey1: "shardKeyValue", _id: 0} + }, + ] + })); + session.commitTransaction(); + session.startTransaction(); + assert.commandFailedWithCode(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [{ + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {myKey: "The Quick Brown Fox"}, + docKey: {myShardKey0: "jumped over", myShardKey1: "the lazy dog", _id: 1234567890} + }] + }), + ErrorCodes.DuplicateKey); + session.abortTransaction(); +} + +// A duplicate key error rolls back the whole bulk write. +{ + const containerEntriesBefore = entriesInContainer(primary, globalIndexUUID); + + session.startTransaction(); + assert.commandWorked(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [{ + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {myKey: "testDupes"}, + docKey: {myShardKey0: "test", _id: "dupes"} + }] + })); + session.commitTransaction(); + + const containerEntriesAfterInsert = entriesInContainer(primary, globalIndexUUID); + assert.eq(containerEntriesAfterInsert, containerEntriesBefore + 1); + + session.startTransaction(); + assert.commandFailedWithCode(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [ + { + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {myKey: "new entry"}, + docKey: {myShardKey0: "new", _id: "entry"} + }, + { + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {myKey: "testDupes"}, + docKey: {myShardKey0: "test", _id: "dupes"} + } + ] + }), + ErrorCodes.DuplicateKey); + session.abortTransaction(); + + const containerEntriesAfterDupKeyError = entriesInContainer(primary, globalIndexUUID); + assert.eq(containerEntriesAfterInsert, containerEntriesAfterDupKeyError); +} + +// A KeyNotFound error rolls back the whole bulk write. +{ + const containerEntriesBefore = entriesInContainer(primary, globalIndexUUID); + + session.startTransaction(); + assert.commandFailedWithCode(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [ + { + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {myKey: "key that will be rolled back"}, + docKey: {myShardKey0: "key", _id: "roll back"} + }, + { + _shardsvrDeleteGlobalIndexKey: globalIndexUUID, + key: {myKey: "key that doesn't exist"}, + docKey: {myShardKey0: "does not", _id: "exist"} + } + ] + }), + ErrorCodes.KeyNotFound); + session.abortTransaction(); + + const containerEntriesAfter = entriesInContainer(primary, globalIndexUUID); + assert.eq(containerEntriesBefore, containerEntriesAfter); +} + +// A KeyNotFound error on the docKey rolls back the whole bulk write. +{ + const containerEntriesBefore = entriesInContainer(primary, globalIndexUUID); + + session.startTransaction(); + assert.commandFailedWithCode(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [ + { + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {myKey: "key that will be rolled back"}, + docKey: {myShardKey0: "key", _id: "roll back"} + }, + { + _shardsvrDeleteGlobalIndexKey: globalIndexUUID, + key: {myKey: "key that will be rolled back"}, + docKey: {myShardKey0: "docKey", _id: "doesn't match"} + } + ] + }), + ErrorCodes.KeyNotFound); + session.abortTransaction(); + + const containerEntriesAfter = entriesInContainer(primary, globalIndexUUID); + assert.eq(containerEntriesBefore, containerEntriesAfter); +} + +// Insert 2k index entries in bulk. +{ + const containerEntriesBefore = entriesInContainer(primary, globalIndexUUID); + let ops = []; + for (let i = 0; i < 2000; i++) { + ops.push( + {_shardsvrInsertGlobalIndexKey: globalIndexUUID, key: {a: i}, docKey: {sk: i, _id: i}}); + } + + session.startTransaction(); + assert.commandWorked( + session.getDatabase("test").runCommand({_shardsvrWriteGlobalIndexKeys: 1, ops: ops})); + session.commitTransaction(); + + const containerEntriesAfter = entriesInContainer(primary, globalIndexUUID); + assert.eq(containerEntriesAfter, containerEntriesBefore + 2000); +} + +// Delete the 2k index entries above in bulk. +{ + const containerEntriesBefore = entriesInContainer(primary, globalIndexUUID); + + let ops = []; + for (let i = 0; i < 2000; i++) { + ops.push( + {_shardsvrDeleteGlobalIndexKey: globalIndexUUID, key: {a: i}, docKey: {sk: i, _id: i}}); + } + + session.startTransaction(); + assert.commandWorked( + session.getDatabase("test").runCommand({_shardsvrWriteGlobalIndexKeys: 1, ops: ops})); + session.commitTransaction(); + + const containerEntriesAfter = entriesInContainer(primary, globalIndexUUID); + assert.eq(containerEntriesAfter, containerEntriesBefore - 2000); +} + +// Insert and delete the same 2k entries in the same bulk. +{ + const containerEntriesBefore = entriesInContainer(primary, globalIndexUUID); + let ops = []; + for (let i = 0; i < 2000; i++) { + ops.push( + {_shardsvrInsertGlobalIndexKey: globalIndexUUID, key: {a: i}, docKey: {sk: i, _id: i}}); + ops.push( + {_shardsvrDeleteGlobalIndexKey: globalIndexUUID, key: {a: i}, docKey: {sk: i, _id: i}}); + } + + session.startTransaction(); + assert.commandWorked( + session.getDatabase("test").runCommand({_shardsvrWriteGlobalIndexKeys: 1, ops: ops})); + session.commitTransaction(); + + const containerEntriesAfter = entriesInContainer(primary, globalIndexUUID); + assert.eq(containerEntriesAfter, containerEntriesBefore); +} + +// Insert and delete keys on multiple global index containers. +{ + const otherGlobalIndexUUID = UUID(); + assert.commandWorked(adminDB.runCommand({_shardsvrCreateGlobalIndex: otherGlobalIndexUUID})); + assert.eq(0, entriesInContainer(primary, otherGlobalIndexUUID)); + + const containerEntriesBefore = entriesInContainer(primary, globalIndexUUID); + const otherContainerEntriesBefore = entriesInContainer(primary, otherGlobalIndexUUID); + + session.startTransaction(); + assert.commandWorked(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [ + { + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {k0: "first"}, + docKey: {sk0: "first", _id: "first"} + }, + { + _shardsvrInsertGlobalIndexKey: otherGlobalIndexUUID, + key: {k0: "firstOnSecondContainer"}, + docKey: {sk0: "firstOnSecondContainer", _id: "firstOnSecondContainer"} + }, + { + _shardsvrInsertGlobalIndexKey: otherGlobalIndexUUID, + key: {k0: "secondOnSecondContainer"}, + docKey: {sk0: "secondOnSecondContainer", _id: "secondOnSecondContainer"} + }, + { + _shardsvrDeleteGlobalIndexKey: globalIndexUUID, + key: {k0: "first"}, + docKey: {sk0: "first", _id: "first"} + }, + { + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {k0: "firstUpdated"}, + docKey: {sk0: "first", _id: "first"} + }, + { + _shardsvrDeleteGlobalIndexKey: otherGlobalIndexUUID, + key: {k0: "firstOnSecondContainer"}, + docKey: {sk0: "firstOnSecondContainer", _id: "firstOnSecondContainer"} + }, + ] + })); + session.commitTransaction(); + + const containerEntriesAfter = entriesInContainer(primary, globalIndexUUID); + const otherContainerEntriesAfter = entriesInContainer(primary, otherGlobalIndexUUID); + assert.eq(containerEntriesAfter, containerEntriesBefore + 1); + assert.eq(otherContainerEntriesAfter, otherContainerEntriesBefore + 1); + + // Verify that that the expected index keys are in place by fetching by document key, and by + // triggering DuplicateKey error on inserting the same index key. + assert.eq(1, + primary.getDB("system") + .getCollection("globalIndexes." + uuidToString(globalIndexUUID)) + .find({_id: {sk0: "first", _id: "first"}}) + .itcount()); + session.startTransaction(); + assert.commandFailedWithCode(session.getDatabase("test").runCommand({ + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {k0: "firstUpdated"}, + docKey: {sk0: "doesn't", _id: "matter"} + }), + ErrorCodes.DuplicateKey); + session.abortTransaction(); + assert.eq(1, + primary.getDB("system") + .getCollection("globalIndexes." + uuidToString(otherGlobalIndexUUID)) + .find({_id: {sk0: "secondOnSecondContainer", _id: "secondOnSecondContainer"}}) + .itcount()); + session.startTransaction(); + assert.commandFailedWithCode(session.getDatabase("test").runCommand({ + _shardsvrInsertGlobalIndexKey: otherGlobalIndexUUID, + key: {k0: "secondOnSecondContainer"}, + docKey: {sk0: "doesn't", _id: "matter"} + }), + ErrorCodes.DuplicateKey); + session.abortTransaction(); +} + +// Insert and delete keys in bulk while inserting a document to a collection in the same multi-doc +// transaction. +{ + const containerEntriesBefore = entriesInContainer(primary, globalIndexUUID); + + session.startTransaction(); + assert.commandWorked(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [ + { + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {k0: "globalIndexKey"}, + docKey: {sk0: "globalIndexKey", _id: "globalIndexKey"} + }, + { + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {k0: "globalIndexKey2"}, + docKey: {sk0: "globalIndexKey2", _id: "globalIndexKey"} + }, + ] + })); + assert.commandWorked(session.getDatabase("test").getCollection("c").insertOne({x: "data"})); + session.commitTransaction(); + + const containerEntriesAfter = entriesInContainer(primary, globalIndexUUID); + assert.eq(containerEntriesAfter, containerEntriesBefore + 2); + + // Verify that that the expected index keys are in place by fetching by document key, and by + // triggering DuplicateKey error on inserting the same index key. + assert.eq(1, + primary.getDB("system") + .getCollection("globalIndexes." + uuidToString(globalIndexUUID)) + .find({_id: {sk0: "globalIndexKey", _id: "globalIndexKey"}}) + .itcount()); + assert.eq(1, + primary.getDB("system") + .getCollection("globalIndexes." + uuidToString(globalIndexUUID)) + .find({_id: {sk0: "globalIndexKey2", _id: "globalIndexKey"}}) + .itcount()); + session.startTransaction(); + assert.commandFailedWithCode(session.getDatabase("test").runCommand({ + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {k0: "globalIndexKey"}, + docKey: {sk0: "doesn't", _id: "matter"} + }), + ErrorCodes.DuplicateKey); + session.abortTransaction(); + session.startTransaction(); + assert.commandFailedWithCode(session.getDatabase("test").runCommand({ + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {k0: "globalIndexKey2"}, + docKey: {sk0: "doesn't", _id: "matter"} + }), + ErrorCodes.DuplicateKey); + session.abortTransaction(); + assert.eq(1, primary.getDB("test").getCollection("c").find({x: "data"}).itcount()); +} + +// A bulk write broken down into multiple applyOps. +{ + const uuid = UUID(); + // Generate three applyOps entries, the last one only containing a single statement. + const stmts = 2 * maxNumberOfTxnOpsInSingleOplogEntry + 1; + assert.commandWorked(adminDB.runCommand({_shardsvrCreateGlobalIndex: uuid})); + assert.eq(0, entriesInContainer(primary, uuid)); + + let ops = []; + for (let i = 0; i < stmts; i++) { + ops.push({_shardsvrInsertGlobalIndexKey: uuid, key: {a: i}, docKey: {sk: i, _id: i}}); + } + session.startTransaction(); + assert.commandWorked( + session.getDatabase("test").runCommand({_shardsvrWriteGlobalIndexKeys: 1, ops: ops})); + session.commitTransaction(); + + // The global index container consists of 'stmts' entries. + assert.eq(stmts, entriesInContainer(primary, uuid)); + + // The transaction is split into three applyOps entries, with 'stmts' entries in total. + const applyOpsSummary = + adminDB.getSiblingDB('local') + .oplog.rs + .aggregate([ + {$match: {ns: "admin.$cmd", 'o.applyOps.op': 'xi', 'o.applyOps.ui': uuid}}, + {$project: {count: {$size: '$o.applyOps'}}}, + ]) + .toArray(); + assert.eq(3, applyOpsSummary.length); + assert.eq(maxNumberOfTxnOpsInSingleOplogEntry, applyOpsSummary[0]["count"]); + assert.eq(maxNumberOfTxnOpsInSingleOplogEntry, applyOpsSummary[1]["count"]); + assert.eq(1, applyOpsSummary[2]["count"]); + assert.eq( + stmts, + applyOpsSummary[0]["count"] + applyOpsSummary[1]["count"] + applyOpsSummary[2]["count"]); +} + +// Global index CRUD ops generate the same oplog entry, regardless of whether the transaction +// statements are bulked together. Also validates the oplog entry. +{ + { + const containerEntriesBefore = entriesInContainer(primary, globalIndexUUID); + session.startTransaction(); + assert.commandWorked(session.getDatabase("test").runCommand({ + _shardsvrWriteGlobalIndexKeys: 1, + ops: [ + { + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {myKey: "insertAndRemove"}, + docKey: {shardKey: "insert", _id: "andRemove"} + }, + { + _shardsvrDeleteGlobalIndexKey: globalIndexUUID, + key: {myKey: "insertAndRemove"}, + docKey: {shardKey: "insert", _id: "andRemove"} + } + ] + })); + session.commitTransaction(); + const containerEntriesAfter = entriesInContainer(primary, globalIndexUUID); + assert.eq(containerEntriesAfter, containerEntriesBefore); + } + { + const containerEntriesBefore = entriesInContainer(primary, globalIndexUUID); + session.startTransaction(); + assert.commandWorked(session.getDatabase("test").runCommand({ + _shardsvrInsertGlobalIndexKey: globalIndexUUID, + key: {myKey: "insertAndRemove"}, + docKey: {shardKey: "insert", _id: "andRemove"} + })); + assert.commandWorked(session.getDatabase("test").runCommand({ + _shardsvrDeleteGlobalIndexKey: globalIndexUUID, + key: {myKey: "insertAndRemove"}, + docKey: {shardKey: "insert", _id: "andRemove"} + })); + session.commitTransaction(); + const containerEntriesAfter = entriesInContainer(primary, globalIndexUUID); + assert.eq(containerEntriesAfter, containerEntriesBefore); + } + + const oplogEntries = + adminDB.getSiblingDB('local') + .oplog.rs + .find({ns: "admin.$cmd", 'o.applyOps.op': 'xi', 'o.applyOps.ui': globalIndexUUID}) + .sort({$natural: -1}) + .limit(2) + .toArray(); + + let oplogEntryBulk = oplogEntries[0]; + let oplogEntryPlain = oplogEntries[1]; + assert.neq(oplogEntryBulk["txnNumber"], oplogEntryPlain["txnNumber"]); + assert.neq(oplogEntryBulk["ts"], oplogEntryPlain["ts"]); + assert.neq(oplogEntryBulk["wall"], oplogEntryPlain["wall"]); + delete oplogEntryBulk.ts; + delete oplogEntryPlain.ts; + delete oplogEntryBulk.txnNumber; + delete oplogEntryPlain.txnNumber; + delete oplogEntryBulk.wall; + delete oplogEntryPlain.wall; + assert.docEq(oplogEntryBulk, oplogEntryPlain); + assert.eq(oplogEntryBulk["o"]["applyOps"][0]["op"], "xi"); + assert.docEq(oplogEntryBulk["o"]["applyOps"][0]["o"]["key"], {myKey: "insertAndRemove"}); + assert.docEq(oplogEntryBulk["o"]["applyOps"][0]["o"]["docKey"], + {shardKey: "insert", _id: "andRemove"}); + assert.eq(oplogEntryBulk["o"]["applyOps"][1]["op"], "xd"); + assert.docEq(oplogEntryBulk["o"]["applyOps"][1]["o"]["key"], {myKey: "insertAndRemove"}); + assert.docEq(oplogEntryBulk["o"]["applyOps"][1]["o"]["docKey"], + {shardKey: "insert", _id: "andRemove"}); +} + +session.endSession(); +rst.stopSet(); +})(); diff --git a/jstests/replsets/all_commands_downgrading_to_upgraded.js b/jstests/replsets/all_commands_downgrading_to_upgraded.js index 9c16dfec889..3d91bcf6c24 100644 --- a/jstests/replsets/all_commands_downgrading_to_upgraded.js +++ b/jstests/replsets/all_commands_downgrading_to_upgraded.js @@ -112,6 +112,7 @@ const allCommands = { _shardsvrGetStatsForBalancing: {skip: isAnInternalCommand}, _shardsvrInsertGlobalIndexKey: {skip: isAnInternalCommand}, _shardsvrDeleteGlobalIndexKey: {skip: isAnInternalCommand}, + _shardsvrWriteGlobalIndexKeys: {skip: isAnInternalCommand}, _shardsvrJoinMigrations: {skip: isAnInternalCommand}, _shardsvrMovePrimary: {skip: isAnInternalCommand}, _shardsvrMoveRange: {skip: isAnInternalCommand}, diff --git a/jstests/replsets/db_reads_while_recovering_all_commands.js b/jstests/replsets/db_reads_while_recovering_all_commands.js index b17ed68ed17..524157d5ca0 100644 --- a/jstests/replsets/db_reads_while_recovering_all_commands.js +++ b/jstests/replsets/db_reads_while_recovering_all_commands.js @@ -99,6 +99,7 @@ const allCommands = { _shardsvrGetStatsForBalancing: {skip: isPrimaryOnly}, _shardsvrInsertGlobalIndexKey: {skip: isPrimaryOnly}, _shardsvrDeleteGlobalIndexKey: {skip: isPrimaryOnly}, + _shardsvrWriteGlobalIndexKeys: {skip: isPrimaryOnly}, _shardsvrJoinMigrations: {skip: isAnInternalCommand}, _shardsvrMovePrimary: {skip: isPrimaryOnly}, _shardsvrMoveRange: {skip: isPrimaryOnly}, diff --git a/jstests/replsets/shardsvr_global_index_ddl_rollback.js b/jstests/replsets/global_index_ddl_rollback.js index 5243f6d4d3d..5243f6d4d3d 100644 --- a/jstests/replsets/shardsvr_global_index_ddl_rollback.js +++ b/jstests/replsets/global_index_ddl_rollback.js diff --git a/jstests/replsets/shardsvr_global_index_crud_rollback.js b/jstests/replsets/shardsvr_global_index_crud_rollback.js index 43c6500a4e0..7e4349c8945 100644 --- a/jstests/replsets/shardsvr_global_index_crud_rollback.js +++ b/jstests/replsets/shardsvr_global_index_crud_rollback.js @@ -81,6 +81,7 @@ rollbackTest.getTestFixture().nodes.forEach(function(node) { // Current test implementation makes fastcount valid due to rolling back both a delete and an // insert. After fixing fast count, we should make this test fail if fast count is not working // properly. +// TODO (SERVER-69847): add a rollback test for _shardsvrWriteGlobalIndexKeys too. rollbackTest.stop(); })(); diff --git a/jstests/sharding/read_write_concern_defaults_application.js b/jstests/sharding/read_write_concern_defaults_application.js index 676fb46567e..d36c36da6cc 100644 --- a/jstests/sharding/read_write_concern_defaults_application.js +++ b/jstests/sharding/read_write_concern_defaults_application.js @@ -157,6 +157,7 @@ let testCases = { _shardsvrGetStatsForBalancing: {skip: "internal command"}, _shardsvrInsertGlobalIndexKey: {skip: "internal command"}, _shardsvrDeleteGlobalIndexKey: {skip: "internal command"}, + _shardsvrWriteGlobalIndexKeys: {skip: "internal command"}, _shardsvrJoinMigrations: {skip: "internal command"}, _shardsvrMovePrimary: {skip: "internal command"}, _shardsvrMoveRange: { |