diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-14 14:26:38 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-14 14:26:38 -0300 |
| commit | 294bc6ecabf14c09c9bc8644704921dcf97cb44e (patch) | |
| tree | 279b1e0bab53901a1647ac63c1c724f0f789a663 /jstests/libs | |
| parent | 70be7c27a251621187a1de533462ae2bb1e3bd39 (diff) | |
| parent | 1e917fd798aa25b7066d4b414b51184f13d5a092 (diff) | |
Update upstream source from tag 'upstream/6.0.10'debian/6.0.10-1
Update to upstream version '6.0.10'
with Debian dir 2d176fa254eee97b139f712fec5709641335a8c3
Diffstat (limited to 'jstests/libs')
17 files changed, 500 insertions, 37 deletions
diff --git a/jstests/libs/analyze_plan.js b/jstests/libs/analyze_plan.js index dcfb3f11221..9ae5e62ba65 100644 --- a/jstests/libs/analyze_plan.js +++ b/jstests/libs/analyze_plan.js @@ -526,9 +526,18 @@ function getPlanCacheKeyFromExplain(explainRes, db) { * Helper to run a explain on the given query shape and get the "planCacheKey" from the explain * result. */ -function getPlanCacheKeyFromShape({query = {}, projection = {}, sort = {}, collection, db}) { - const explainRes = - assert.commandWorked(collection.explain().find(query, projection).sort(sort).finish()); +function getPlanCacheKeyFromShape({ + query = {}, + projection = {}, + sort = {}, + collation = { + locale: "simple" + }, + collection, + db +}) { + const explainRes = assert.commandWorked( + collection.explain().find(query, projection).collation(collation).sort(sort).finish()); return getPlanCacheKeyFromExplain(explainRes, db); } diff --git a/jstests/libs/api_version_helpers.js b/jstests/libs/api_version_helpers.js index 363d89e6d32..c4b0529769a 100644 --- a/jstests/libs/api_version_helpers.js +++ b/jstests/libs/api_version_helpers.js @@ -51,9 +51,9 @@ var APIVersionHelpers = (function() { * Asserts that the given pipeline cannot be used to define a view when apiStrict is set to true * and apiVersion is "1" on the create command. */ - function assertViewFailsWithAPIStrict(pipeline, collName) { + function assertViewFailsWithAPIStrict(pipeline, viewName, collName) { assert.commandFailedWithCode(db.runCommand({ - create: 'new_50_feature_view', + create: viewName, viewOn: collName, pipeline: pipeline, apiStrict: true, @@ -67,16 +67,16 @@ var APIVersionHelpers = (function() { * Asserts that the given pipeline can be used to define a view when apiStrict is set to true * and apiVersion is "1" on the create command. */ - function assertViewSucceedsWithAPIStrict(pipeline, collName) { + function assertViewSucceedsWithAPIStrict(pipeline, viewName, collName) { assert.commandWorked(db.runCommand({ - create: 'new_50_feature_view', + create: viewName, viewOn: collName, pipeline: pipeline, apiStrict: true, apiVersion: "1" })); - assert.commandWorked(db.runCommand({drop: 'new_50_feature_view'})); + assert.commandWorked(db.runCommand({drop: viewName})); } return { diff --git a/jstests/libs/change_stream_rewrite_util.js b/jstests/libs/change_stream_rewrite_util.js index ebbb0937222..918873c17bc 100644 --- a/jstests/libs/change_stream_rewrite_util.js +++ b/jstests/libs/change_stream_rewrite_util.js @@ -138,7 +138,10 @@ function assertNumMatchingOplogEventsForShard(stats, shardName, expectedTotalRet assert(stats.shards.hasOwnProperty(shardName), stats); assert.eq(Object.keys(stats.shards[shardName].stages[0])[0], "$cursor", stats); const executionStats = stats.shards[shardName].stages[0].$cursor.executionStats; - assert.eq(executionStats.nReturned, expectedTotalReturned, executionStats); + assert.eq(executionStats.nReturned, + expectedTotalReturned, + () => `Expected ${expectedTotalReturned} events on shard ${shardName} but got ` + + `${executionStats.nReturned}. Execution stats:\n${tojson(executionStats)}`); } // Returns a newly created sharded collection sharded by caller provided shard key. @@ -180,7 +183,10 @@ function verifyChangeStreamOnWholeCluster( eventIdentifierList.forEach(eventIdentifier => { assert.soon(() => cursor.hasNext(), {op: op, eventIdentifier: eventIdentifier}); const event = cursor.next(); - assert.eq(event.operationType, op, event); + assert.eq(event.operationType, + op, + () => `Expected "${op}" but got "${event.operationType}". Full event: ` + + `${tojson(event)}`); if (op == "dropDatabase") { assert.eq(event.ns.db, eventIdentifier, event); diff --git a/jstests/libs/cluster_server_parameter_utils.js b/jstests/libs/cluster_server_parameter_utils.js index f50b487c40a..2b6c93a5d54 100644 --- a/jstests/libs/cluster_server_parameter_utils.js +++ b/jstests/libs/cluster_server_parameter_utils.js @@ -116,6 +116,9 @@ function setupSharded(st) { shards.forEach(function(shard) { setupReplicaSet(shard); }); + + // Wait for FCV to fully replicate on all shards before performing test commands. + st.awaitReplicationOnShards(); } // Upserts config.clusterParameters document with w:majority via setClusterParameter. diff --git a/jstests/libs/clustered_collections/clustered_capped_utils.js b/jstests/libs/clustered_collections/clustered_capped_utils.js index 8e6ae836223..e9c136f57fd 100644 --- a/jstests/libs/clustered_collections/clustered_capped_utils.js +++ b/jstests/libs/clustered_collections/clustered_capped_utils.js @@ -1,3 +1,5 @@ +load("jstests/libs/ttl_util.js"); + var ClusteredCappedUtils = class { // Validate TTL-based deletion on a clustered, capped collection. static testClusteredCappedCollectionWithTTL(db, collName, clusterKeyField) { @@ -39,7 +41,8 @@ var ClusteredCappedUtils = class { } assert.commandWorked(coll.insertMany(docs, {ordered: true})); - ClusteredCollectionUtil.waitForTTL(db); + // This test runs with default read concern 'local'. + TTLUtil.waitForPass(db, /*waitForMajorityCommit=*/false); // Only the recent documents survived. assert.eq(coll.find().itcount(), batchSize); @@ -140,7 +143,7 @@ var ClusteredCappedUtils = class { // TTL delete the two old documents. assert.commandWorked(db.adminCommand({setParameter: 1, ttlMonitorEnabled: true})); - ClusteredCollectionUtil.waitForTTL(db); + TTLUtil.waitForPass(db, /*waitForMajorityCommit=*/isReplicated); assert.eq(2, db.getCollection(collName).find().itcount()); // Confirm that the tailable getMore can resume from where it was, since the document the @@ -209,7 +212,7 @@ var ClusteredCappedUtils = class { // TTL delete the two old documents, while the tailable cursor is still on the first one. assert.commandWorked(db.adminCommand({setParameter: 1, ttlMonitorEnabled: true})); - ClusteredCollectionUtil.waitForTTL(db); + TTLUtil.waitForPass(db, /*waitForMajorityCommit=*/isReplicated); assert.eq(1, db.getCollection(collName).find().itcount()); // Confirm that the tailable cursor returns CappedPositionLost, as the document it was @@ -292,7 +295,8 @@ var ClusteredCappedUtils = class { // Expire the document. assert.commandWorked(db.adminCommand({setParameter: 1, ttlMonitorEnabled: true})); - ClusteredCollectionUtil.waitForTTL(db); + // No need to wait for majority commit, as default 'local' read concern is used. + TTLUtil.waitForPass(db, /*waitForMajorityCommit=*/false); assert.eq(0, db.getCollection(collName).find().itcount()); // The TTL deletion has been replicated to the oplog. diff --git a/jstests/libs/clustered_collections/clustered_collection_bounded_scan_common.js b/jstests/libs/clustered_collections/clustered_collection_bounded_scan_common.js index b81717c7552..f9495c913c5 100644 --- a/jstests/libs/clustered_collections/clustered_collection_bounded_scan_common.js +++ b/jstests/libs/clustered_collections/clustered_collection_bounded_scan_common.js @@ -94,6 +94,23 @@ const testClusteredCollectionBoundedScan = function(coll, clusterKey) { assert.eq(expectedNReturned, expl.executionStats.executionStages.nReturned); assert.eq(expectedDocsExamined, expl.executionStats.executionStages.docsExamined); } + function testIn() { + initAndPopulate(coll, clusterKey); + + const expl = assert.commandWorked(coll.getDB().runCommand({ + explain: {find: coll.getName(), filter: {[clusterKeyFieldName]: {$in: [10, 20, 30]}}}, + verbosity: "executionStats" + })); + + assert(getPlanStage(expl, "CLUSTERED_IXSCAN")); + assert.eq(10, getPlanStage(expl, "CLUSTERED_IXSCAN").minRecord); + assert.eq(30, getPlanStage(expl, "CLUSTERED_IXSCAN").maxRecord); + + assert.eq(3, expl.executionStats.executionStages.nReturned); + // The range scanned is 21 documents + 1 extra document by design - additional cursor + // 'next' beyond the range. + assert.eq(22, expl.executionStats.executionStages.docsExamined); + } function testNonClusterKeyScan() { initAndPopulate(coll, clusterKey); @@ -128,6 +145,8 @@ const testClusteredCollectionBoundedScan = function(coll, clusterKey) { testRange("$gte", 20, "$lt", 40, 20, 22); testRange("$gt", 20, "$lte", 40, 20, 22); testRange("$gte", 20, "$lte", 40, 21, 22); + testIn(); + testNonClusterKeyScan(); } diff --git a/jstests/libs/clustered_collections/clustered_collection_util.js b/jstests/libs/clustered_collections/clustered_collection_util.js index ad6c0111c8a..c63b5ce56e9 100644 --- a/jstests/libs/clustered_collections/clustered_collection_util.js +++ b/jstests/libs/clustered_collections/clustered_collection_util.js @@ -196,14 +196,4 @@ var ClusteredCollectionUtil = class { assert.eq(1, coll.find({[clusterKey]: NumberLong("42")}).itcount()); coll.drop(); } - - static waitForTTL(db) { - // The 'ttl.passes' metric is incremented when the TTL monitor starts processing the - // indexes, so we wait for it to be incremented twice to know that the TTL monitor finished - // processing the indexes at least once. - const ttlPasses = db.serverStatus().metrics.ttl.passes; - assert.soon(function() { - return db.serverStatus().metrics.ttl.passes > ttlPasses + 1; - }); - } }; diff --git a/jstests/libs/command_line/test_parsed_options.js b/jstests/libs/command_line/test_parsed_options.js index 5c32f3a8774..e4400952760 100644 --- a/jstests/libs/command_line/test_parsed_options.js +++ b/jstests/libs/command_line/test_parsed_options.js @@ -76,6 +76,8 @@ function testGetCmdLineOptsMongod(mongoRunnerConfig, expectedResult) { typeof expectedResult.parsed.storage.dbPath === "undefined") { delete getCmdLineOptsExpected.parsed.storage.dbPath; } + // Delete backtraceLogFile parameter, since we are generating unique value every time + delete getCmdLineOptsExpected.parsed.setParameter.backtraceLogFile; // Merge with the result that we expect expectedResult = mergeOptions(getCmdLineOptsExpected, expectedResult); @@ -108,6 +110,8 @@ function testGetCmdLineOptsMongod(mongoRunnerConfig, expectedResult) { typeof expectedResult.parsed.storage.dbPath === "undefined") { delete getCmdLineOptsResult.parsed.storage.dbPath; } + // Delete backtraceLogFile parameter, since we are generating unique value every time + delete getCmdLineOptsResult.parsed.setParameter.backtraceLogFile; // Make sure the options are equal to what we expect assert.docEq(getCmdLineOptsResult.parsed, expectedResult.parsed); diff --git a/jstests/libs/conn_pool_helpers.js b/jstests/libs/conn_pool_helpers.js new file mode 100644 index 00000000000..9beb49da1d1 --- /dev/null +++ b/jstests/libs/conn_pool_helpers.js @@ -0,0 +1,64 @@ +load("jstests/libs/parallelTester.js"); + +function configureReplSetFailpoint(st, kDbName, failpoint, modeValue) { + st.rs0.nodes.forEach(function(node) { + assert.commandWorked(node.getDB("admin").runCommand({ + configureFailPoint: failpoint, + mode: modeValue, + data: { + shouldCheckForInterrupt: true, + nss: kDbName + ".test", + }, + })); + }); +} + +function launchFinds(mongos, threads, {times, readPref, shouldFail}) { + jsTestLog("Starting " + times + " connections"); + for (var i = 0; i < times; i++) { + var thread = new Thread(function(connStr, readPref, dbName, shouldFail) { + var client = new Mongo(connStr); + const ret = client.getDB(dbName).runCommand( + {find: "test", limit: 1, "$readPreference": {mode: readPref}}); + + if (shouldFail) { + assert.commandFailed(ret); + } else { + assert.commandWorked(ret); + } + }, mongos.host, readPref, 'test', shouldFail); + thread.start(); + threads.push(thread); + } +} + +function assertHasConnPoolStats(mongos, allHosts, args, checkNum, connPoolStatsCmd = undefined) { + checkNum++; + jsTestLog("Check #" + checkNum + ": " + tojson(args)); + let {ready = 0, pending = 0, active = 0, hosts = allHosts, isAbsent, checkStatsFunc} = args; + checkStatsFunc = checkStatsFunc ? checkStatsFunc : function(stats) { + return stats.available == ready && stats.refreshing == pending && + (stats.inUse + stats.leased) == active; + }; + + function checkStats(res, host) { + let stats = res.hosts[host]; + if (!stats) { + jsTestLog("Connection stats for " + host + " are absent"); + return isAbsent; + } + + jsTestLog("Connection stats for " + host + ": " + tojson(stats)); + return checkStatsFunc(stats); + } + + function checkAllStats() { + let cmdName = connPoolStatsCmd ? connPoolStatsCmd : "connPoolStats"; + let res = mongos.adminCommand({[cmdName]: 1}); + return hosts.map(host => checkStats(res, host)).every(x => x); + } + + assert.soon(checkAllStats, "Check #" + checkNum + " failed", 10000); + jsTestLog("Check #" + checkNum + " successful"); + return checkNum; +} diff --git a/jstests/libs/index_catalog_helpers.js b/jstests/libs/index_catalog_helpers.js new file mode 100644 index 00000000000..a61267fbbbd --- /dev/null +++ b/jstests/libs/index_catalog_helpers.js @@ -0,0 +1,85 @@ +"use strict"; + +/** + * Helper functions that help test things to do with the index catalog. + */ +var IndexCatalogHelpers = (function() { + /** + * Returns the index specification with the name 'indexName' if it is present in the + * 'indexSpecs' array, and returns null otherwise. + */ + function getIndexSpecByName(indexSpecs, indexName) { + if (typeof indexName !== "string") { + throw new Error("'indexName' parameter must be a string, but got " + tojson(indexName)); + } + + const found = indexSpecs.filter(spec => spec.name === indexName); + + if (found.length > 1) { + throw new Error("Found multiple indexes with name '" + indexName + + "': " + tojson(indexSpecs)); + } + return (found.length === 1) ? found[0] : null; + } + + /** + * Returns the index specification with the key pattern 'keyPattern' and the collation + * 'collation' if it is present in the 'indexSpecs' array, and returns null otherwise. + * + * The 'collation' parameter is optional and is only required to be specified when multiple + * indexes with the same key pattern exist. + */ + function getIndexSpecByKeyPattern(indexSpecs, keyPattern, collation) { + const collationWasSpecified = arguments.length >= 3; + const foundByKeyPattern = indexSpecs.filter(spec => { + return bsonWoCompare(spec.key, keyPattern) === 0; + }); + + if (!collationWasSpecified) { + if (foundByKeyPattern.length > 1) { + throw new Error( + "Found multiple indexes with key pattern " + tojson(keyPattern) + + " and 'collation' parameter was not specified: " + tojson(indexSpecs)); + } + return (foundByKeyPattern.length === 1) ? foundByKeyPattern[0] : null; + } + + const foundByKeyPatternAndCollation = foundByKeyPattern.filter(spec => { + if (collation.locale === "simple") { + // The simple collation is not explicitly stored in the index catalog, so we expect + // the "collation" field to be absent. + return !spec.hasOwnProperty("collation"); + } + return bsonWoCompare(spec.collation, collation) === 0; + }); + + if (foundByKeyPatternAndCollation.length > 1) { + throw new Error("Found multiple indexes with key pattern" + tojson(keyPattern) + + " and collation " + tojson(collation) + ": " + tojson(indexSpecs)); + } + return (foundByKeyPatternAndCollation.length === 1) ? foundByKeyPatternAndCollation[0] + : null; + } + + function createSingleIndex(coll, key, parameters) { + return coll.getDB().runCommand( + {createIndexes: coll.getName(), indexes: [Object.assign({key: key}, parameters)]}); + } + + function createIndexAndVerifyWithDrop(coll, key, parameters) { + coll.dropIndexes(); + assert.commandWorked(createSingleIndex(coll, key, parameters)); + assert.neq( + null, + getIndexSpecByName(coll.getIndexes(), parameters.name), + () => + `Could not find index with name ${parameters.name}: ${tojson(coll.getIndexes())}`); + } + + return { + findByName: getIndexSpecByName, + findByKeyPattern: getIndexSpecByKeyPattern, + createSingleIndex: createSingleIndex, + createIndexAndVerifyWithDrop: createIndexAndVerifyWithDrop, + }; +})(); diff --git a/jstests/libs/os_helpers.js b/jstests/libs/os_helpers.js new file mode 100644 index 00000000000..f7cafa2d898 --- /dev/null +++ b/jstests/libs/os_helpers.js @@ -0,0 +1,211 @@ +/** + * Test helpers for identifying OS + */ + +function isLinux() { + return getBuildInfo().buildEnvironment.target_os == "linux"; +} + +function isMacOS() { + return getBuildInfo().buildEnvironment.target_os == "macOS"; +} + +// See "man 5 os-release" for documentation +function readOsRelease() { + try { + const os_release = cat("/etc/os-release"); + + let lines = os_release.split("\n"); + + let tags = {}; + + for (let line of lines) { + let vp = line.replaceAll("\"", "").split("="); + tags[vp[0]] = vp[1]; + } + + return tags; + } catch (ignored) { + // ignore + } + + assert(!isLinux(), "Linux hosts should always have /etc/os-release."); + + return {}; +} + +/** + * Check if Linux OS is given identifier. Identifiers are always lower case strings. + * + * @param {string} distro ID of the distro in os-release + * @returns + */ +function isDistro(distro) { + let tags = readOsRelease(); + return tags.hasOwnProperty("ID") && tags["ID"] === distro; +} + +/** + * Check if Linux OS is given identifier and specific version. Do not use for matching major + * versions like RHEL 8, isRHELMajorVerison. + * + * @param {string} distro ID of the distro in os-release + * @returns + */ +function isDistroVersion(distro, version) { + let tags = readOsRelease(); + return tags.hasOwnProperty("ID") && tags["ID"] === distro && + tags.hasOwnProperty("VERSION_ID") && tags["VERSION_ID"] === version; +} + +/** + * Is it RHEL and is it 7, 8 or 9? + * @param {string} majorVersion + * @returns True if majorVersion = 8 and version is 8.1, 8.2 etc. + */ +function isRHELMajorVerison(majorVersion) { + let tags = readOsRelease(); + return tags.hasOwnProperty("ID") && tags["ID"] === "rhel" && + tags.hasOwnProperty("VERSION_ID") && tags["VERSION_ID"].startsWith(majorVersion); +} + +/** + * Example +NAME="Red Hat Enterprise Linux" +VERSION="8.7 (Ootpa)" +ID="rhel" +ID_LIKE="fedora" +VERSION_ID="8.7" +PLATFORM_ID="platform:el8" +PRETTY_NAME="Red Hat Enterprise Linux 8.7 (Ootpa)" +ANSI_COLOR="0;31" +CPE_NAME="cpe:/o:redhat:enterprise_linux:8::baseos" +HOME_URL="https://www.redhat.com/" +DOCUMENTATION_URL="https://access.redhat.com/documentation/red_hat_enterprise_linux/8/" +BUG_REPORT_URL="https://bugzilla.redhat.com/" + +REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 8" +REDHAT_BUGZILLA_PRODUCT_VERSION=8.7 +REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux" +REDHAT_SUPPORT_PRODUCT_VERSION="8.7" + */ +function isRHEL8() { + // RHEL 8 disables TLS 1.0 and TLS 1.1 as part their default crypto policy + // We skip tests on RHEL 8 that require these versions as a result. + return isRHELMajorVerison("8"); +} + +function isSUSE15SP1() { + if (_isWindows()) { + return false; + } + + // SUSE 15 SP1 FIPS module does not work. SP2 does work. + // The FIPS code returns FIPS_R_IN_ERROR_STATE in what is likely a race condition + // since it only happens in sharded clusters. + const grep_result = runProgram('grep', '15-SP1', '/etc/os-release'); + if (grep_result == 0) { + return true; + } + + return false; +} + +function isUbuntu() { + // Ubuntu 18.04 and later compiles openldap against gnutls which does not + // support SHA1 signed certificates. ldaptest.10gen.cc uses a SHA1 cert. + return isDistro("ubuntu"); +} + +/** + * Example: +NAME="Ubuntu" +VERSION="18.04.6 LTS (Bionic Beaver)" +ID=ubuntu +ID_LIKE=debian +PRETTY_NAME="Ubuntu 18.04.6 LTS" +VERSION_ID="18.04" +HOME_URL="https://www.ubuntu.com/" +SUPPORT_URL="https://help.ubuntu.com/" +BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" +PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" +VERSION_CODENAME=bionic +UBUNTU_CODENAME=bionic + */ +function isUbuntu1804() { + // Ubuntu 18.04's TLS 1.3 implementation has an issue with OCSP stapling. We have disabled + // stapling on this build variant, so we need to ensure that tests that require stapling + // do not run on this machine. + return isDistroVersion("ubuntu", "18.04"); +} + +function isUbuntu2004() { + // Ubuntu 20.04 disables TLS 1.0 and TLS 1.1 as part their default crypto policy + // We skip tests on Ubuntu 20.04 that require these versions as a result. + return isDistroVersion("ubuntu", "20.04"); +} + +/** + * Example: +PRETTY_NAME="Debian GNU/Linux 12 (bookworm)" +NAME="Debian GNU/Linux" +VERSION_ID="12" +VERSION="12 (bookworm)" +VERSION_CODENAME=bookworm +ID=debian +HOME_URL="https://www.debian.org/" +SUPPORT_URL="https://www.debian.org/support" +BUG_REPORT_URL="https://bugs.debian.org/" + */ +function isDebian() { + return isDistro("debian"); +} + +/** + * Example: +NAME="Fedora Linux" +VERSION="38 (Workstation Edition)" +ID=fedora +VERSION_ID=38 +VERSION_CODENAME="" +PLATFORM_ID="platform:f38" +PRETTY_NAME="Fedora Linux 38 (Workstation Edition)" +ANSI_COLOR="0;38;2;60;110;180" +LOGO=fedora-logo-icon +CPE_NAME="cpe:/o:fedoraproject:fedora:38" +DEFAULT_HOSTNAME="fedora" +HOME_URL="https://fedoraproject.org/" +DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f38/system-administrators-guide/" +SUPPORT_URL="https://ask.fedoraproject.org/" +BUG_REPORT_URL="https://bugzilla.redhat.com/" +REDHAT_BUGZILLA_PRODUCT="Fedora" +REDHAT_BUGZILLA_PRODUCT_VERSION=38 +REDHAT_SUPPORT_PRODUCT="Fedora" +REDHAT_SUPPORT_PRODUCT_VERSION=38 +SUPPORT_END=2024-05-14 +VARIANT="Workstation Edition" +VARIANT_ID=workstation + */ +function isFedora() { + return isDistro("fedora"); +} + +/** + * Note: Amazon 2022 was never released for production. It became Amazon 2023. + * + * Example: +NAME="Amazon Linux" +VERSION="2022" +ID="amzn" +ID_LIKE="fedora" +VERSION_ID="2022" +PLATFORM_ID="platform:al2022" +PRETTY_NAME="Amazon Linux 2022" +ANSI_COLOR="0;33" +CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2022" +HOME_URL="https://aws.amazon.com/linux/" +BUG_REPORT_URL="https://github.com/amazonlinux/amazon-linux-2022" +*/ +function isAmazon2023() { + return isDistroVersion("amzn", "2022") || isDistroVersion("amzn", "2023"); +} diff --git a/jstests/libs/override_methods/config_fuzzer_incompatible_commands.js b/jstests/libs/override_methods/config_fuzzer_incompatible_commands.js new file mode 100644 index 00000000000..b20e5accfe9 --- /dev/null +++ b/jstests/libs/override_methods/config_fuzzer_incompatible_commands.js @@ -0,0 +1,34 @@ +/** + * Overrides commands that are incompatible to run when the config fuzzer is enabled. + */ +(function() { +"use strict"; + +load("jstests/libs/override_methods/override_helpers.js"); + +function runCommandOverride(conn, dbName, commandName, commandObj, func, makeFuncArgs) { + if (commandName == "compact") { + // A single compact can perform up to 10-20 checkpoints - which can cause tests to time out + // when mongod configurations stress the server. + throw new Error( + "Cowardly refusing to run test that uses command 'compact' with the config fuzzer enabled. " + + tojson(commandObj)); + } + + if (commandName == "emptycapped") { + // The emptycapped command is a test only command, so we do not expect users to run it with + // different WiredTiger configurations on mongod. + throw new Error( + "Cowardly refusing to run test that uses command 'emptycapped' with the config fuzzer enabled. " + + tojson(commandObj)); + } + + const serverResponse = func.apply(conn, makeFuncArgs(commandObj)); + return serverResponse; +} + +OverrideHelpers.prependOverrideInParallelShell( + "jstests/libs/override_methods/config_fuzzer_incompatible_commands.js"); + +OverrideHelpers.overrideRunCommand(runCommandOverride); +})(); diff --git a/jstests/libs/override_methods/implicitly_shard_accessed_collections.js b/jstests/libs/override_methods/implicitly_shard_accessed_collections.js index 8356f0bfb62..c0ec4649505 100644 --- a/jstests/libs/override_methods/implicitly_shard_accessed_collections.js +++ b/jstests/libs/override_methods/implicitly_shard_accessed_collections.js @@ -197,6 +197,11 @@ DB.prototype.getCollection = function() { try { TestData.doNotOverrideReadPreference = true; collStats = this.runCommand({collStats: collection.getName()}); + if (!collStats.ok && collStats.codeName == "CommandNotSupportedOnView") { + // In case we catch CommandNotSupportedOnView it means the collection was actually a + // view and should be returned without attempting to shard it (which is not allowed) + return collection; + } } finally { TestData.doNotOverrideReadPreference = testDataDoNotOverrideReadPreferenceOriginal; } diff --git a/jstests/libs/override_methods/implicitly_wrap_pipelines_in_facets.js b/jstests/libs/override_methods/implicitly_wrap_pipelines_in_facets.js index 92199015faf..0d4e0158b3e 100644 --- a/jstests/libs/override_methods/implicitly_wrap_pipelines_in_facets.js +++ b/jstests/libs/override_methods/implicitly_wrap_pipelines_in_facets.js @@ -67,7 +67,7 @@ Mongo.prototype.runCommand = function(dbName, cmdObj, options) { } cmdObj.pipeline = [ - {$facet: {originalPipeline: originalPipeline}}, + {$facet: {originalPipeline: originalPipeline, extraPipeline: [{$count: "count"}]}}, {$unwind: '$originalPipeline'}, {$replaceRoot: {newRoot: '$originalPipeline'}}, ]; diff --git a/jstests/libs/override_methods/network_error_and_txn_override.js b/jstests/libs/override_methods/network_error_and_txn_override.js index fc1ec89b266..46e6cc27d5e 100644 --- a/jstests/libs/override_methods/network_error_and_txn_override.js +++ b/jstests/libs/override_methods/network_error_and_txn_override.js @@ -844,18 +844,6 @@ function shouldRetryWithNetworkErrorOverride( return kContinue; } - // listCollections and listIndexes called through mongos may return OperationFailed if - // the request to establish a cursor on the targeted shard fails with a network error. - // - // TODO SERVER-30949: Remove this check once those two commands retry on retryable - // errors automatically. - if ((cmdName === "listCollections" || cmdName === "listIndexes") && - res.code === ErrorCodes.OperationFailed && res.hasOwnProperty("errmsg") && - res.errmsg.indexOf("failed to read command response from shard") >= 0) { - logError("Retrying failed mongos cursor command"); - return kContinue; - } - // Some sharding commands return raw responses from all contacted shards and there won't // be a top level code if shards returned more than one error code, in which case retry // if any error is retryable. diff --git a/jstests/libs/parallelTester.js b/jstests/libs/parallelTester.js index c0a31e54b86..6241c455ff9 100644 --- a/jstests/libs/parallelTester.js +++ b/jstests/libs/parallelTester.js @@ -306,6 +306,7 @@ if (typeof _threadInject != "undefined") { parallelFilesDir + "/profile_sampling.js", parallelFilesDir + "/profile_update.js", parallelFilesDir + "/cached_plan_trial_does_not_discard_work.js", + parallelFilesDir + "/timeseries/bucket_unpacking_with_sort_plan_cache.js", // These tests rely on a deterministically refreshable logical session cache. If they // run in parallel, they could interfere with the cache and cause failures. diff --git a/jstests/libs/ttl_util.js b/jstests/libs/ttl_util.js new file mode 100644 index 00000000000..59c67be7486 --- /dev/null +++ b/jstests/libs/ttl_util.js @@ -0,0 +1,40 @@ +/** + * Utilities for testing TTL collections. + */ + +load("jstests/libs/fixture_helpers.js"); + +const TTLUtil = class { + /** + * Wait until documents inserted before a call to this function have been visited by a TTL + * monitor pass. On replica sets, by default the function waits for the TTL deletes to become + * visible with read concern 'majority'. + * + * @param {DB} db Database connection. + * @param {boolean} waitForMajorityCommit Only applies when 'db' is from a replica set, set to + * false to disable waiting for TTL deletes to become majority commited. + */ + static waitForPass(db, waitForMajorityCommit = true) { + // The 'ttl.passes' metric is incremented when the TTL monitor has finished a pass. + // Depending on the timing of the pass, seeing an increment of this metric might not + // necessarily imply the data we are expecting to be deleted has been seen, as the TTL pass + // might have been in progress while the data was inserted. Waiting to see two increases of + // this metric guarantees that the TTL has started a new pass after test data insertion. + const ttlPasses = db.serverStatus().metrics.ttl.passes; + assert.soon(function() { + return db.serverStatus().metrics.ttl.passes > ttlPasses + 1; + }); + + // Readers using a "majority" read concern might expect TTL deletes to be visible after + // waitForPass. TTL writes do not imply 'majority' nor 'j: true', and are made durable by + // the journal flusher when a flush cycle happens every 'commitIntervalMs'. Even in single + // node replica sets, depending on journal flush timing, it is possible that TTL deletes + // have not been made durable after returning from this function, and are not considered + // majority commited. We force the majority commit point to include the TTL writes up to + // this point in time. + if (FixtureHelpers.isReplSet(db) && waitForMajorityCommit) { + // waitForMajorityCommit will never be true if 'db' is not part of a replica set. + FixtureHelpers.awaitLastOpCommitted(db); + } + } +}; |
