diff options
author | Kaitlin Mahar <kaitlin.mahar@mongodb.com> | 2023-02-16 15:50:47 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-02-16 17:57:16 +0000 |
commit | ddf2bfb6a3e3b1a17723e77166690eb6f00ad36d (patch) | |
tree | 3b8b561bed2a153be92e59efd226f96fe358e7e3 /jstests | |
parent | f74fb285c7eeeb24d2284b980a6b5df977803b3b (diff) | |
download | mongo-ddf2bfb6a3e3b1a17723e77166690eb6f00ad36d.tar.gz |
SERVER-60064 Make create command idempotent on mongod
Diffstat (limited to 'jstests')
-rw-r--r-- | jstests/core/ddl/create_collection.js | 104 | ||||
-rw-r--r-- | jstests/core/views/invalid_view_prevents_creating_new_view.js | 49 | ||||
-rw-r--r-- | jstests/core/views/views_creation.js | 78 | ||||
-rw-r--r-- | jstests/replsets/noop_writes_wait_for_write_concern.js | 9 |
4 files changed, 185 insertions, 55 deletions
diff --git a/jstests/core/ddl/create_collection.js b/jstests/core/ddl/create_collection.js index a9273f0c948..a38d9efb970 100644 --- a/jstests/core/ddl/create_collection.js +++ b/jstests/core/ddl/create_collection.js @@ -1,10 +1,8 @@ -// Cannot implicitly shard accessed collections because of collection existing when none -// expected. // FCV4.4 is required for creating a collection with a long name. // @tags: [ -// assumes_against_mongod_not_mongos, -// assumes_no_implicit_collection_creation_after_drop, // requires_capped, +// # TODO SERVER-73967: Remove this tag. +// does_not_support_stepdowns, // ] // Tests for the "create" command. @@ -12,9 +10,15 @@ "use strict"; load("jstests/libs/index_catalog_helpers.js"); +load("jstests/libs/clustered_collections/clustered_collection_util.js"); + +// TODO SERVER-73934: Change assertions on 'drop' command results throughout this file to +// always expect the command worked. Currently, they can return NamespaceNotFound on +// server versions < 7.0. // "create" command rejects invalid options. -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandFailedWithCode(db.createCollection("create_collection", {unknown: 1}), 40415); // Cannot create a collection with null characters. @@ -25,7 +29,8 @@ assert.commandFailedWithCode(db.createCollection("ab\0"), ErrorCodes.InvalidName // The collection name length limit was upped in 4.4, try creating a collection with a longer // name than previously allowed. const longCollName = 'a'.repeat(200); -db[longCollName].drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: longCollName}), + ErrorCodes.NamespaceNotFound); assert.commandWorked(db.createCollection(longCollName)); // @@ -33,7 +38,8 @@ assert.commandWorked(db.createCollection(longCollName)); // // "idIndex" field not allowed with "viewOn". -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandWorked(db.createCollection("create_collection")); assert.commandFailedWithCode(db.runCommand({ create: "create_view", @@ -43,36 +49,42 @@ assert.commandFailedWithCode(db.runCommand({ ErrorCodes.InvalidOptions); // "idIndex" field not allowed with "autoIndexId". -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandFailedWithCode( db.createCollection("create_collection", {autoIndexId: false, idIndex: {key: {_id: 1}, name: "_id_"}}), ErrorCodes.InvalidOptions); // "idIndex" field must be an object. -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandFailedWithCode(db.createCollection("create_collection", {idIndex: 1}), ErrorCodes.TypeMismatch); // "idIndex" field cannot be empty. -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandFailedWithCode(db.createCollection("create_collection", {idIndex: {}}), ErrorCodes.FailedToParse); // "idIndex" field must be a specification for an _id index. -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandFailedWithCode( db.createCollection("create_collection", {idIndex: {key: {a: 1}, name: "a_1"}}), ErrorCodes.BadValue); // "idIndex" field must have "key" equal to {_id: 1}. -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandFailedWithCode( db.createCollection("create_collection", {idIndex: {key: {a: 1}, name: "_id_"}}), ErrorCodes.BadValue); // The name of an _id index gets corrected to "_id_". -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandWorked( db.createCollection("create_collection", {idIndex: {key: {_id: 1}, name: "a_1"}})); var indexSpec = IndexCatalogHelpers.findByKeyPattern(db.create_collection.getIndexes(), {_id: 1}); @@ -80,14 +92,16 @@ assert.neq(indexSpec, null); assert.eq(indexSpec.name, "_id_", tojson(indexSpec)); // "idIndex" field must only contain fields that are allowed for an _id index. -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandFailedWithCode( db.createCollection("create_collection", {idIndex: {key: {_id: 1}, name: "_id_", sparse: true}}), ErrorCodes.InvalidIndexSpecificationOption); // "create" creates v=2 _id index when "v" is not specified in "idIndex". -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandWorked( db.createCollection("create_collection", {idIndex: {key: {_id: 1}, name: "_id_"}})); indexSpec = IndexCatalogHelpers.findByName(db.create_collection.getIndexes(), "_id_"); @@ -95,7 +109,8 @@ assert.neq(indexSpec, null); assert.eq(indexSpec.v, 2, tojson(indexSpec)); // "create" creates v=1 _id index when "idIndex" has "v" equal to 1. -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandWorked( db.createCollection("create_collection", {idIndex: {key: {_id: 1}, name: "_id_", v: 1}})); indexSpec = IndexCatalogHelpers.findByName(db.create_collection.getIndexes(), "_id_"); @@ -103,7 +118,8 @@ assert.neq(indexSpec, null); assert.eq(indexSpec.v, 1, tojson(indexSpec)); // "create" creates v=2 _id index when "idIndex" has "v" equal to 2. -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandWorked( db.createCollection("create_collection", {idIndex: {key: {_id: 1}, name: "_id_", v: 2}})); indexSpec = IndexCatalogHelpers.findByName(db.create_collection.getIndexes(), "_id_"); @@ -111,27 +127,31 @@ assert.neq(indexSpec, null); assert.eq(indexSpec.v, 2, tojson(indexSpec)); // "collation" field of "idIndex" must match collection default collation. -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandFailedWithCode( db.createCollection("create_collection", {idIndex: {key: {_id: 1}, name: "_id_", collation: {locale: "en_US"}}}), ErrorCodes.BadValue); -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandFailedWithCode(db.createCollection("create_collection", { collation: {locale: "fr_CA"}, idIndex: {key: {_id: 1}, name: "_id_", collation: {locale: "en_US"}} }), ErrorCodes.BadValue); -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandFailedWithCode(db.createCollection("create_collection", { collation: {locale: "fr_CA"}, idIndex: {key: {_id: 1}, name: "_id_", collation: {locale: "simple"}} }), ErrorCodes.BadValue); -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandWorked(db.createCollection("create_collection", { collation: {locale: "en_US", strength: 3}, idIndex: {key: {_id: 1}, name: "_id_", collation: {locale: "en_US"}} @@ -142,7 +162,8 @@ assert.eq(indexSpec.collation.locale, "en_US", tojson(indexSpec)); // If "collation" field is not present in "idIndex", _id index inherits collection default // collation. -db.create_collection.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); assert.commandWorked(db.createCollection( "create_collection", {collation: {locale: "en_US"}, idIndex: {key: {_id: 1}, name: "_id_"}})); indexSpec = IndexCatalogHelpers.findByName(db.create_collection.getIndexes(), "_id_"); @@ -158,11 +179,14 @@ assert.commandFailedWithCode(db.createCollection('capped_no_size_no_max', {cappe ErrorCodes.InvalidOptions); assert.commandFailedWithCode(db.createCollection('capped_no_size', {capped: true, max: 10}), ErrorCodes.InvalidOptions); -db.no_capped.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "no_capped"}), + ErrorCodes.NamespaceNotFound); assert.commandWorked(db.createCollection('no_capped'), {capped: false}); -db.capped_no_max.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "capped_no_max"}), + ErrorCodes.NamespaceNotFound); assert.commandWorked(db.createCollection('capped_no_max', {capped: true, size: 256})); -db.capped_with_max_and_size.drop(); +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "capped_with_max_and_size"}), + ErrorCodes.NamespaceNotFound); assert.commandWorked( db.createCollection('capped_with_max_and_size', {capped: true, max: 10, size: 256})); @@ -171,4 +195,34 @@ assert.commandFailedWithCode(db.createCollection('size_no_capped', {size: 256}), ErrorCodes.InvalidOptions); assert.commandFailedWithCode(db.createCollection('size_capped_false', {capped: false, size: 256}), ErrorCodes.InvalidOptions); + +// The remainder of this test file will not work if all collections are automatically clustered +// because a repeat attempt to create a collection will not have ``clusteredIndex`` set but +// the existing collection will. +if (ClusteredCollectionUtil.areAllCollectionsClustered(db.getMongo())) { + return; +} + +// The remainder of this test will not work on server versions < 7.0 as the 'create' command +// is not idempotent there. TODO SERVER-74062: remove this. +if (db.version().split('.')[0] < 7) { + return; +} + +assert.commandWorkedOrFailedWithCode(db.runCommand({drop: "create_collection"}), + ErrorCodes.NamespaceNotFound); + +// Creating a collection that already exists with no options specified reports success. +assert.commandWorked(db.createCollection("create_collection")); +assert.commandWorked(db.createCollection("create_collection")); + +assert.commandWorked(db.runCommand({drop: "create_collection"})); + +// Creating a collection that already exists with the same options reports success. +assert.commandWorked(db.createCollection("create_collection"), {collation: {locale: "fr"}}); +assert.commandWorked(db.createCollection("create_collection"), {collation: {locale: "fr"}}); + +// Creating a collection that already exists with different options reports failure. +assert.commandFailedWithCode(db.createCollection("create_collection", {collation: {locale: "uk"}}), + ErrorCodes.NamespaceExists); })(); diff --git a/jstests/core/views/invalid_view_prevents_creating_new_view.js b/jstests/core/views/invalid_view_prevents_creating_new_view.js new file mode 100644 index 00000000000..c9134938938 --- /dev/null +++ b/jstests/core/views/invalid_view_prevents_creating_new_view.js @@ -0,0 +1,49 @@ +/** + * Test that, when an existing view in system.views is invalid because of a $out in the + * pipeline, the database errors on creation of a new view. + * + * The test runs commands that are not allowed with security token: applyOps. + * @tags: [ + * not_allowed_with_security_token, + * # applyOps is not available on mongos. + * assumes_against_mongod_not_mongos, + * assumes_superuser_permissions, + * # applyOps is not retryable. + * requires_non_retryable_commands, + * # Tenant migrations don't support applyOps. + * tenant_migration_incompatible, + * ] + */ +(function() { +"use strict"; + +// For arrayEq. +load("jstests/aggregation/extras/utils.js"); + +const viewsDBName = jsTestName(); + +let viewsDB = db.getSiblingDB(viewsDBName); +assert.commandWorked(viewsDB.dropDatabase()); + +// Create an initial collection and view so the DB and system.views collection exist. +assert.commandWorked(viewsDB.runCommand({create: "collection"})); +assert.commandWorked(viewsDB.runCommand({create: "testView", viewOn: "collection"})); + +assert.commandWorked(viewsDB.adminCommand({ + applyOps: [{ + op: "i", + ns: viewsDBName + ".system.views", + o: { + _id: viewsDBName + ".invalidView", + viewOn: "collection", + pipeline: [{$project: {_id: false}}, {$out: "notExistingCollection"}], + } + }] +})); +assert.commandFailedWithCode( + viewsDB.runCommand({create: "viewWithBadViewCatalog", viewOn: "collection", pipeline: []}), + ErrorCodes.OptionNotSupportedOnView); +assert.commandWorked(viewsDB.adminCommand({ + applyOps: [{op: "d", ns: viewsDBName + ".system.views", o: {_id: viewsDBName + ".invalidView"}}] +})); +}()); diff --git a/jstests/core/views/views_creation.js b/jstests/core/views/views_creation.js index e7b9baa3484..fa6c8eb7594 100644 --- a/jstests/core/views/views_creation.js +++ b/jstests/core/views/views_creation.js @@ -1,18 +1,10 @@ /** * Test the creation of views with various options. * - * The test runs commands that are not allowed with security token: applyOps. * @tags: [ - * not_allowed_with_security_token, - * # Commands on views not supported in implicitly sharded suites. - * assumes_unsharded_collection, - * # applyOps is not available on mongos. - * assumes_against_mongod_not_mongos, * assumes_superuser_permissions, - * # applyOps is not retryable. - * requires_non_retryable_commands, - * # Tenant migrations don't support applyOps. - * tenant_migration_incompatible, + * # TODO SERVER-73967: Remove this tag. + * does_not_support_stepdowns, * ] */ (function() { @@ -119,23 +111,51 @@ assert.commandFailedWithCode(viewsDB.runCommand({ }), 40600); -// These test that, when an existing view in system.views is invalid because of a $out in the -// pipeline, the database errors on creation of a new view. -assert.commandWorked(viewsDB.adminCommand({ - applyOps: [{ - op: "i", - ns: viewsDBName + ".system.views", - o: { - _id: viewsDBName + ".invalidView", - viewOn: "collection", - pipeline: [{$project: {_id: false}}, {$out: "notExistingCollection"}] - } - }] -})); -assert.commandFailedWithCode( - viewsDB.runCommand({create: "viewWithBadViewCatalog", viewOn: "collection", pipeline: []}), - ErrorCodes.OptionNotSupportedOnView); -assert.commandWorked(viewsDB.adminCommand({ - applyOps: [{op: "d", ns: viewsDBName + ".system.views", o: {_id: viewsDBName + ".invalidView"}}] -})); +// The remainder of this test will not work on server versions < 7.0 as the 'create' command +// is not idempotent there. TODO SERVER-74062: remove this. +if (db.version().split('.')[0] < 7) { + return; +} + +// Test that creating a view which already exists with identical options reports success. +let repeatedCmd = { + create: "existingViewTest", + viewOn: "collection", + pipeline: [{$match: {x: 1}}], + collation: {locale: "uk"}, +}; +assert.commandWorked(viewsDB.runCommand(repeatedCmd)); +assert.commandWorked(viewsDB.runCommand(repeatedCmd)); + +// Test that creating a view with the same name as an existing view but different options fails. + +// Different collation. +assert.commandFailedWithCode(viewsDB.runCommand({ + create: "existingViewTest", + viewOn: "collection", + pipeline: [{$match: {x: 1}}], + collation: {locale: "fr"}, +}), + ErrorCodes.NamespaceExists); + +// Different pipeline. +assert.commandFailedWithCode(viewsDB.runCommand({ + create: "existingViewTest", + viewOn: "collection", + pipeline: [{$match: {x: 2}}], + collation: {locale: "uk"}, +}), + ErrorCodes.NamespaceExists); +// viewOn collection is different. +assert.commandFailedWithCode(viewsDB.runCommand({ + create: "existingViewTest", + viewOn: "collection1", + pipeline: [{$match: {x: 1}}], + collation: {locale: "uk"}, +}), + ErrorCodes.NamespaceExists); + +// Test that creating a view when there is already a collection with the same name fails. +assert.commandFailedWithCode(viewsDB.runCommand({create: "collection", viewOn: "collection"}), + ErrorCodes.NamespaceExists); }()); diff --git a/jstests/replsets/noop_writes_wait_for_write_concern.js b/jstests/replsets/noop_writes_wait_for_write_concern.js index 1fc4bfdcb13..828ace51871 100644 --- a/jstests/replsets/noop_writes_wait_for_write_concern.js +++ b/jstests/replsets/noop_writes_wait_for_write_concern.js @@ -233,7 +233,14 @@ commands.push({ assert.commandWorkedIgnoringWriteConcernErrors(db.runCommand({create: collName})); }, confirmFunc: function(res) { - assert.commandFailedWithCode(res, ErrorCodes.NamespaceExists); + // Branching is needed for multiversion tests as 'create' is only idempotent as of 7.0. + // TODO SERVER-74062: update this to stop branching on the server version and always + // assert the command worked ignoring write concern errors. + if (db.version().split('.')[0] >= 7) { + assert.commandWorkedIgnoringWriteConcernErrors(res); + } else { + assert.commandFailedWithCode(res, ErrorCodes.NamespaceExists); + } } }); |