From dd6e504fffa225b2b39adfc8369a208084a73650 Mon Sep 17 00:00:00 2001 From: Patrick Freed Date: Thu, 17 Oct 2019 16:28:25 -0400 Subject: [PATCH 1/6] add retryable reads runner --- Sources/MongoSwift/MongoClient.swift | 7 +- Sources/MongoSwift/MongoCollection.swift | 2 +- Tests/LinuxMain.swift | 9 +- Tests/MongoSwiftTests/ChangeStreamTests.swift | 2 +- .../MongoSwiftTests/RetryableReadsTests.swift | 72 + .../RetryableWritesTests.swift | 43 +- .../SpecTestRunner/Match.swift | 3 + .../SpecTestRunner/SpecTest.swift | 233 +++- .../SpecTestRunner/SpecTestUtils.swift | 13 +- .../SpecTestRunner/TestOperation.swift | 428 ++++-- .../SpecTestRunner/TestOperationResult.swift | 12 +- Tests/Scripts/add_json_files.rb | 1 + .../tests/aggregate-merge.json | 98 ++ .../tests/aggregate-serverErrors.json | 1207 +++++++++++++++++ .../retryable-reads/tests/aggregate.json | 405 ++++++ ...angeStreams-client.watch-serverErrors.json | 738 ++++++++++ .../tests/changeStreams-client.watch.json | 207 +++ ...ngeStreams-db.coll.watch-serverErrors.json | 688 ++++++++++ .../tests/changeStreams-db.coll.watch.json | 195 +++ .../changeStreams-db.watch-serverErrors.json | 688 ++++++++++ .../tests/changeStreams-db.watch.json | 195 +++ .../tests/count-serverErrors.json | 585 ++++++++ Tests/Specs/retryable-reads/tests/count.json | 178 +++ .../tests/countDocuments-serverErrors.json | 910 +++++++++++++ .../retryable-reads/tests/countDocuments.json | 256 ++++ .../tests/distinct-serverErrors.json | 837 ++++++++++++ .../Specs/retryable-reads/tests/distinct.json | 244 ++++ .../estimatedDocumentCount-serverErrors.json | 546 ++++++++ .../tests/estimatedDocumentCount.json | 166 +++ .../tests/find-serverErrors.json | 961 +++++++++++++ Tests/Specs/retryable-reads/tests/find.json | 347 +++++ .../tests/findOne-serverErrors.json | 731 ++++++++++ .../Specs/retryable-reads/tests/findOne.json | 222 +++ .../tests/gridfs-download-serverErrors.json | 924 +++++++++++++ .../tests/gridfs-download.json | 269 ++++ .../gridfs-downloadByName-serverErrors.json | 848 ++++++++++++ .../tests/gridfs-downloadByName.json | 249 ++++ .../listCollectionNames-serverErrors.json | 501 +++++++ .../tests/listCollectionNames.json | 149 ++ .../listCollectionObjects-serverErrors.json | 501 +++++++ .../tests/listCollectionObjects.json | 149 ++ .../tests/listCollections-serverErrors.json | 501 +++++++ .../tests/listCollections.json | 149 ++ .../tests/listDatabaseNames-serverErrors.json | 501 +++++++ .../tests/listDatabaseNames.json | 149 ++ .../listDatabaseObjects-serverErrors.json | 501 +++++++ .../tests/listDatabaseObjects.json | 149 ++ .../tests/listDatabases-serverErrors.json | 501 +++++++ .../retryable-reads/tests/listDatabases.json | 149 ++ .../tests/listIndexNames-serverErrors.json | 526 +++++++ .../retryable-reads/tests/listIndexNames.json | 155 +++ .../tests/listIndexes-serverErrors.json | 526 +++++++ .../retryable-reads/tests/listIndexes.json | 155 +++ .../retryable-reads/tests/mapReduce.json | 187 +++ 54 files changed, 18782 insertions(+), 186 deletions(-) create mode 100644 Tests/MongoSwiftTests/RetryableReadsTests.swift create mode 100644 Tests/Specs/retryable-reads/tests/aggregate-merge.json create mode 100644 Tests/Specs/retryable-reads/tests/aggregate-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/aggregate.json create mode 100644 Tests/Specs/retryable-reads/tests/changeStreams-client.watch-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/changeStreams-client.watch.json create mode 100644 Tests/Specs/retryable-reads/tests/changeStreams-db.coll.watch-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/changeStreams-db.coll.watch.json create mode 100644 Tests/Specs/retryable-reads/tests/changeStreams-db.watch-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/changeStreams-db.watch.json create mode 100644 Tests/Specs/retryable-reads/tests/count-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/count.json create mode 100644 Tests/Specs/retryable-reads/tests/countDocuments-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/countDocuments.json create mode 100644 Tests/Specs/retryable-reads/tests/distinct-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/distinct.json create mode 100644 Tests/Specs/retryable-reads/tests/estimatedDocumentCount-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/estimatedDocumentCount.json create mode 100644 Tests/Specs/retryable-reads/tests/find-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/find.json create mode 100644 Tests/Specs/retryable-reads/tests/findOne-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/findOne.json create mode 100644 Tests/Specs/retryable-reads/tests/gridfs-download-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/gridfs-download.json create mode 100644 Tests/Specs/retryable-reads/tests/gridfs-downloadByName-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/gridfs-downloadByName.json create mode 100644 Tests/Specs/retryable-reads/tests/listCollectionNames-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/listCollectionNames.json create mode 100644 Tests/Specs/retryable-reads/tests/listCollectionObjects-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/listCollectionObjects.json create mode 100644 Tests/Specs/retryable-reads/tests/listCollections-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/listCollections.json create mode 100644 Tests/Specs/retryable-reads/tests/listDatabaseNames-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/listDatabaseNames.json create mode 100644 Tests/Specs/retryable-reads/tests/listDatabaseObjects-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/listDatabaseObjects.json create mode 100644 Tests/Specs/retryable-reads/tests/listDatabases-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/listDatabases.json create mode 100644 Tests/Specs/retryable-reads/tests/listIndexNames-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/listIndexNames.json create mode 100644 Tests/Specs/retryable-reads/tests/listIndexes-serverErrors.json create mode 100644 Tests/Specs/retryable-reads/tests/listIndexes.json create mode 100644 Tests/Specs/retryable-reads/tests/mapReduce.json diff --git a/Sources/MongoSwift/MongoClient.swift b/Sources/MongoSwift/MongoClient.swift index 944658a3a..069fd8152 100644 --- a/Sources/MongoSwift/MongoClient.swift +++ b/Sources/MongoSwift/MongoClient.swift @@ -33,8 +33,7 @@ public struct ClientOptions: CodingStrategyProvider, Decodable { public var readPreference: ReadPreference? = nil /// Determines whether the client should retry supported read operations. - // TODO: SWIFT-587 make this public. - internal var retryReads: Bool? + public var retryReads: Bool? /// Determines whether the client should retry supported write operations. public var retryWrites: Bool? @@ -67,7 +66,7 @@ public struct ClientOptions: CodingStrategyProvider, Decodable { public var writeConcern: WriteConcern? private enum CodingKeys: CodingKey { - case retryWrites, readConcern, writeConcern + case retryWrites, retryReads, readConcern, writeConcern } /// Convenience initializer allowing any/all to be omitted or optional. @@ -78,6 +77,7 @@ public struct ClientOptions: CodingStrategyProvider, Decodable { notificationCenter: NotificationCenter? = nil, readConcern: ReadConcern? = nil, readPreference: ReadPreference? = nil, + retryReads: Bool? = nil, retryWrites: Bool? = nil, serverMonitoring: Bool = false, tlsOptions: TLSOptions? = nil, @@ -91,6 +91,7 @@ public struct ClientOptions: CodingStrategyProvider, Decodable { self.readConcern = readConcern self.readPreference = readPreference self.retryWrites = retryWrites + self.retryReads = retryReads self.serverMonitoring = serverMonitoring self.tlsOptions = tlsOptions self.uuidCodingStrategy = uuidCodingStrategy diff --git a/Sources/MongoSwift/MongoCollection.swift b/Sources/MongoSwift/MongoCollection.swift index ffbb394fb..b6b2911b2 100644 --- a/Sources/MongoSwift/MongoCollection.swift +++ b/Sources/MongoSwift/MongoCollection.swift @@ -17,7 +17,7 @@ public struct SyncMongoCollection { internal let _client: SyncMongoClient /// The namespace for this collection. - private let namespace: MongoNamespace + internal let namespace: MongoNamespace /// Encoder used by this collection for BSON conversions. (e.g. converting `CollectionType`s, indexes, and options /// to documents). diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index b5a15eec6..8d530d621 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -1,4 +1,4 @@ -// Generated using Sourcery 0.16.1 — https://github.com/krzysztofzablocki/Sourcery +// Generated using Sourcery 0.16.2 — https://github.com/krzysztofzablocki/Sourcery // DO NOT EDIT @@ -276,6 +276,12 @@ extension ReadWriteConcernTests { ] } +extension RetryableReadsTests { + static var allTests = [ + ("testRetryableReads", testRetryableReads), + ] +} + extension RetryableWritesTests { static var allTests = [ ("testRetryableWrites", testRetryableWrites), @@ -310,6 +316,7 @@ XCTMain([ testCase(OptionsTests.allTests), testCase(ReadPreferenceTests.allTests), testCase(ReadWriteConcernTests.allTests), + testCase(RetryableReadsTests.allTests), testCase(RetryableWritesTests.allTests), testCase(SDAMTests.allTests), ]) diff --git a/Tests/MongoSwiftTests/ChangeStreamTests.swift b/Tests/MongoSwiftTests/ChangeStreamTests.swift index 0f0ce7f96..417fb72d8 100644 --- a/Tests/MongoSwiftTests/ChangeStreamTests.swift +++ b/Tests/MongoSwiftTests/ChangeStreamTests.swift @@ -69,7 +69,7 @@ internal struct ChangeStreamTestOperation: Decodable { internal func execute(using client: SyncMongoClient) throws -> TestOperationResult? { let db = client.db(self.database) let coll = db.collection(self.collection) - return try self.operation.op.execute(client: client, database: db, collection: coll, session: nil) + return try self.operation.execute(on: .collection(coll), session: nil) } } diff --git a/Tests/MongoSwiftTests/RetryableReadsTests.swift b/Tests/MongoSwiftTests/RetryableReadsTests.swift new file mode 100644 index 000000000..3a644602c --- /dev/null +++ b/Tests/MongoSwiftTests/RetryableReadsTests.swift @@ -0,0 +1,72 @@ +import Foundation +import MongoSwift +import Nimble +import XCTest + +/// Struct representing a single test within a spec test JSON file. +private struct RetryableReadsTest: SpecTest { + let description: String + + let operations: [TestOperationDescription] + + let clientOptions: ClientOptions? + + let useMultipleMongoses: Bool? + + let skipReason: String? + + let failPoint: FailPoint? + + let expectations: [TestCommandStartedEvent]? +} + +/// Struct representing a single retryable-writes spec test JSON file. +private struct RetryableReadsTestFile: Decodable, SpecTestFile { + private enum CodingKeys: String, CodingKey { + case name, runOn, databaseName = "database_name", collectionName = "collection_name", data, tests + } + + let name: String + + let runOn: [TestRequirement]? + + let databaseName: String + + let collectionName: String? + + let data: TestData + + let tests: [RetryableReadsTest] +} + +final class RetryableReadsTests: MongoSwiftTestCase, FailPointConfigured { + var activeFailPoint: FailPoint? + + override func tearDown() { + self.disableActiveFailPoint() + } + + override func setUp() { + self.continueAfterFailure = false + } + + func testRetryableReads() throws { + let skippedTestKeywords = [ + "findOne", + "estimatedDocumentCount", + "changeStream", // TODO: Unskip this test once CDRIVER-3405 is resolved + "gridfs", + "countDocuments", + "mapReduce" + ] + + let tests = try retrieveSpecTestFiles(specName: "retryable-reads", asType: RetryableReadsTestFile.self) + for (_, testFile) in tests { + guard skippedTestKeywords.allSatisfy({ !testFile.name.contains($0) }) else { + fileLevelLog("Skipping tests from file \(testFile.name)...") + continue + } + try testFile.runTests(parent: self) + } + } +} diff --git a/Tests/MongoSwiftTests/RetryableWritesTests.swift b/Tests/MongoSwiftTests/RetryableWritesTests.swift index 2a54f4aa8..6dd6c2954 100644 --- a/Tests/MongoSwiftTests/RetryableWritesTests.swift +++ b/Tests/MongoSwiftTests/RetryableWritesTests.swift @@ -3,10 +3,15 @@ import Foundation import Nimble import XCTest -/// Struct representing a single test within a spec test JSON file. -private struct RetryableWritesTest: Decodable, SpecTest { +/// Struct representing a single test within a retryable-writes spec test JSON file. +private struct RetryableWritesTest: Decodable { + /// Description of the test. let description: String + + /// The expected outcome of executing the operation. let outcome: TestOutcome + + /// The operation to execute as part of this test case. let operation: AnyTestOperation /// Options used to configure the `SyncMongoClient` used for this test. @@ -67,12 +72,12 @@ final class RetryableWritesTests: MongoSwiftTestCase, FailPointConfigured { if let requirements = testFile.runOn { guard requirements.contains(where: { $0.isMet(by: version, MongoSwiftTestCase.topologyType) }) else { - print("Skipping tests from file \(fileName), deployment requirements not met.") + fileLevelLog("Skipping tests from file \(fileName), deployment requirements not met.") continue } } - print("\n------------\nExecuting tests from file \(fileName)...\n") + fileLevelLog("Executing tests from file \(fileName)...\n") for test in testFile.tests { print("Executing test: \(test.description)") @@ -90,7 +95,35 @@ final class RetryableWritesTests: MongoSwiftTestCase, FailPointConfigured { } defer { self.disableActiveFailPoint() } - try test.run(client: client, db: db, collection: collection, session: nil) + var result: TestOperationResult? + var seenError: Error? + + do { + result = try test.operation.execute(on: .collection(collection), session: nil) + } catch { + if case let ServerError.bulkWriteError(_, _, _, bulkResult, _) = error { + result = TestOperationResult(from: bulkResult) + } + seenError = error + } + + if test.outcome.error ?? false { + expect(seenError).toNot(beNil(), description: test.description) + } else { + expect(seenError).to(beNil(), description: test.description) + } + + if let expectedResult = test.outcome.result { + expect(result).toNot(beNil()) + expect(result).to(equal(expectedResult)) + } + + let verifyColl = db.collection(test.outcome.collection.name ?? collection.name) + let foundDocs = try Array(verifyColl.find()) + expect(foundDocs.count).to(equal(test.outcome.collection.data.count)) + zip(foundDocs, test.outcome.collection.data).forEach { + expect($0).to(sortedEqual($1), description: test.description) + } } } } diff --git a/Tests/MongoSwiftTests/SpecTestRunner/Match.swift b/Tests/MongoSwiftTests/SpecTestRunner/Match.swift index 52bbf808e..795cc547f 100644 --- a/Tests/MongoSwiftTests/SpecTestRunner/Match.swift +++ b/Tests/MongoSwiftTests/SpecTestRunner/Match.swift @@ -90,6 +90,9 @@ extension BSON: Matchable { case let (.array(actual), .array(expected)): return actual.matches(expected: expected) default: + if let selfInt = self.asInt(), let expectedInt = expected.asInt() { + return selfInt == expectedInt + } return self == expected } } diff --git a/Tests/MongoSwiftTests/SpecTestRunner/SpecTest.swift b/Tests/MongoSwiftTests/SpecTestRunner/SpecTest.swift index 8ba73e7fe..5ac16603b 100644 --- a/Tests/MongoSwiftTests/SpecTestRunner/SpecTest.swift +++ b/Tests/MongoSwiftTests/SpecTestRunner/SpecTest.swift @@ -9,7 +9,7 @@ internal struct TestCommandStartedEvent: Decodable, Matchable { let commandName: String - let databaseName: String + let databaseName: String? internal enum CodingKeys: String, CodingKey { case command, commandName = "command_name", databaseName = "database_name" @@ -29,13 +29,26 @@ internal struct TestCommandStartedEvent: Decodable, Matchable { let container = try decoder.container(keyedBy: TopLevelCodingKeys.self) let eventContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .type) self.command = try eventContainer.decode(Document.self, forKey: .command) - self.commandName = try eventContainer.decode(String.self, forKey: .commandName) - self.databaseName = try eventContainer.decode(String.self, forKey: .databaseName) + if let commandName = try eventContainer.decodeIfPresent(String.self, forKey: .commandName) { + self.commandName = commandName + } else if let firstKey = self.command.keys.first { + self.commandName = firstKey + } else { + throw DecodingError.keyNotFound( + CodingKeys.commandName, + DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "commandName not found") + ) + } + self.databaseName = try eventContainer.decodeIfPresent(String.self, forKey: .databaseName) } internal func contentMatches(expected: TestCommandStartedEvent) -> Bool { + if let expectedDbName = expected.databaseName { + guard let dbName = self.databaseName, dbName.matches(expected: expectedDbName) else { + return false + } + } return self.commandName.matches(expected: expected.commandName) - && self.databaseName.matches(expected: expected.databaseName) && self.command.matches(expected: expected.command) } } @@ -254,6 +267,41 @@ internal struct TestRequirement: Decodable { } } +/// Enum representing the contents of deployment before a spec test has been run. +internal enum TestData: Decodable { + /// Data for multiple collections, with the name of the collection mapping to its contents. + case multiple([String: [Document]]) + + /// The contents of a single collection. + case single([Document]) + + public init(from decoder: Decoder) throws { + if let array = try? [Document](from: decoder) { + self = .single(array) + } else if let document = try? Document(from: decoder) { + var mapping: [String: [Document]] = [:] + for (k, v) in document { + guard let documentArray = v.arrayValue?.asArrayOf(Document.self) else { + throw DecodingError.typeMismatch( + [Document].self, + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected array of documents, got \(v) instead" + ) + ) + } + mapping[k] = documentArray + } + self = .multiple(mapping) + } else { + throw DecodingError.typeMismatch( + TestData.self, + DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode `TestData`") + ) + } + } +} + /// Struct representing the contents of a collection after a spec test has been run. internal struct CollectionTestInfo: Decodable { /// An optional name specifying a collection whose documents match the `data` field of this struct. @@ -276,60 +324,155 @@ internal struct TestOutcome: Decodable { let collection: CollectionTestInfo } +/// Protocol defining the behavior of an entire spec test file. +internal protocol SpecTestFile: Decodable { + associatedtype TestType: SpecTest + + /// The name of the file. + /// This field must be added to the file this test is decoded from. + var name: String { get } + + /// Server version and topology requirements in order for tests from this file to be run. + var runOn: [TestRequirement]? { get } + + /// The database to use for testing. + var databaseName: String { get } + + /// The collection to use for testing. + var collectionName: String? { get } + + /// Data that should exist in the collection before running any of the tests. + var data: TestData { get } + + /// List of tests to run in this file. + var tests: [TestType] { get } +} + +extension SpecTestFile { + /// Populate the database and collection specified by this test file using the provided client. + internal func populateData(using client: SyncMongoClient) throws { + let database = client.db(self.databaseName) + + try? database.drop() + + switch self.data { + case let .single(docs): + guard let collName = self.collectionName else { + throw UserError.invalidArgumentError(message: "missing collection name") + } + + guard !docs.isEmpty else { + return + } + + try database.collection(collName).insertMany(docs) + case let .multiple(mapping): + for (k, v) in mapping { + guard !v.isEmpty else { + continue + } + try database.collection(k).insertMany(v) + } + } + } + + /// Run all the tests specified in this file, optionally specifying keywords that, if included in a test's + /// description, will cause certain tests to be skipped. + internal func runTests(parent: FailPointConfigured, skippedTestKeywords: [String] = []) throws { + let setupClient = try SyncMongoClient.makeTestClient() + let version = try setupClient.serverVersion() + + if let requirements = self.runOn { + guard requirements.contains(where: { $0.isMet(by: version, MongoSwiftTestCase.topologyType) }) else { + fileLevelLog("Skipping tests from file \(self.name), deployment requirements not met.") + return + } + } + + try self.populateData(using: setupClient) + + fileLevelLog("Executing tests from file \(self.name)...") + for test in self.tests { + guard skippedTestKeywords.allSatisfy({ !test.description.contains($0) }) else { + print("Skipping test \(test.description)") + return + } + try test.run(parent: parent, dbName: self.databaseName, collName: self.collectionName, session: nil) + } + } +} + /// Protocol defining the behavior of an individual spec test. -internal protocol SpecTest { +internal protocol SpecTest: Decodable { + /// The name of the test. var description: String { get } - var outcome: TestOutcome { get } - var operation: AnyTestOperation { get } - - /// Runs the operation with the given context and performs assertions on the result based upon the expected outcome. - func run( - client: SyncMongoClient, - db: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? - ) throws + + /// Options used to configure the `SyncMongoClient` used for this test. + var clientOptions: ClientOptions? { get } + + /// If true, the `SyncMongoClient` for this test should be initialized with multiple mongos seed addresses. + /// If false or omitted, only a single mongos address should be specified. + /// This field has no effect for non-sharded topologies. + var useMultipleMongoses: Bool? { get } + + /// Reason why this test should be skipped. + var skipReason: String? { get } + + /// The optional fail point to configure before running this test. + /// This option and useMultipleMongoses: true are mutually exclusive. + var failPoint: FailPoint? { get } + + /// + var operations: [TestOperationDescription] { get } + + /// List of expected CommandStartedEvents. + var expectations: [TestCommandStartedEvent]? { get } } /// Default implementation of a test execution. extension SpecTest { internal func run( - client: SyncMongoClient, - db: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? + parent: FailPointConfigured, + dbName: String, + collName: String?, + session _: SyncClientSession? ) throws { - var result: TestOperationResult? - var seenError: Error? - do { - result = try self.operation.op.execute( - client: client, - database: db, - collection: collection, - session: session - ) - } catch { - if case let ServerError.bulkWriteError(_, _, _, bulkResult, _) = error { - result = TestOperationResult(from: bulkResult) - } - seenError = error + guard self.skipReason == nil else { + print("Skipping test for reason: \(self.skipReason!)") + return } - if self.outcome.error ?? false { - expect(seenError).toNot(beNil(), description: self.description) - } else { - expect(seenError).to(beNil(), description: self.description) + print("Executing test: \(self.description)") + + var clientOptions = self.clientOptions ?? ClientOptions(retryReads: true) + clientOptions.commandMonitoring = self.expectations != nil + + if let failPoint = self.failPoint { + try parent.activateFailPoint(failPoint) } + defer { parent.disableActiveFailPoint() } + + let client = try SyncMongoClient.makeTestClient(options: clientOptions) + let db: SyncMongoDatabase = client.db(dbName) + var collection: SyncMongoCollection? - if let expectedResult = self.outcome.result { - expect(result).toNot(beNil()) - expect(result).to(equal(expectedResult)) + if let collName = collName { + collection = db.collection(collName) } - let verifyColl = db.collection(self.outcome.collection.name ?? collection.name) - let foundDocs = try Array(verifyColl.find()) - expect(foundDocs.count).to(equal(self.outcome.collection.data.count)) - zip(foundDocs, self.outcome.collection.data).forEach { - expect($0).to(sortedEqual($1), description: self.description) + + let events = try captureCommandEvents(from: client, eventTypes: [.commandStarted]) { + for operation in self.operations { + try operation.validateExecution( + client: client, + database: db, + collection: collection, + session: nil + ) + } + }.map { TestCommandStartedEvent(from: $0 as! CommandStartedEvent) } + + if let expectations = self.expectations { + expect(events).to(match(expectations), description: self.description) } } } diff --git a/Tests/MongoSwiftTests/SpecTestRunner/SpecTestUtils.swift b/Tests/MongoSwiftTests/SpecTestRunner/SpecTestUtils.swift index 0e7512a8f..8a79c9dce 100644 --- a/Tests/MongoSwiftTests/SpecTestRunner/SpecTestUtils.swift +++ b/Tests/MongoSwiftTests/SpecTestRunner/SpecTestUtils.swift @@ -40,9 +40,12 @@ internal func retrieveSpecTestFiles( return try FileManager.default .contentsOfDirectory(atPath: path) .filter { $0.hasSuffix(".json") } - .map { ($0, URL(fileURLWithPath: "\(path)/\($0)")) } - .map { ($0.0, try Document(fromJSONFile: $0.1)) } - .map { ($0.0, try BSONDecoder().decode(T.self, from: $0.1)) } + .map { filename in + let url = URL(fileURLWithPath: "\(path)/\(filename)") + var doc = try Document(fromJSONFile: url) + doc["name"] = .string(filename) + return try (filename, BSONDecoder().decode(T.self, from: doc)) + } } /// Given two documents, returns a copy of the input document with all keys that *don't* @@ -70,3 +73,7 @@ internal func rearrangeDoc(_ input: Document, toLookLike standard: Document) -> } return output } + +internal func fileLevelLog(_ message: String) { + print("\n------------\n\(message)\n") +} diff --git a/Tests/MongoSwiftTests/SpecTestRunner/TestOperation.swift b/Tests/MongoSwiftTests/SpecTestRunner/TestOperation.swift index 1926c62a4..65b95931e 100644 --- a/Tests/MongoSwiftTests/SpecTestRunner/TestOperation.swift +++ b/Tests/MongoSwiftTests/SpecTestRunner/TestOperation.swift @@ -1,18 +1,97 @@ @testable import MongoSwift +import Nimble + +/// A enumeration of the different objects a `TestOperation` may be performed against. +enum TestOperationObject: String, Decodable { + case client, database, collection, gridfsbucket +} + +/// Struct containing an operation and an expected outcome. +struct TestOperationDescription: Decodable { + /// The operation to run. + let operation: AnyTestOperation + + /// The object to perform the operation on. + let object: TestOperationObject + + /// The return value of the operation, if any. + let result: TestOperationResult? + + /// Whether the operation should expect an error. + let error: Bool? + + public enum CodingKeys: CodingKey { + case object, result, error + } + + public init(from decoder: Decoder) throws { + self.operation = try AnyTestOperation(from: decoder) + + let container = try decoder.container(keyedBy: CodingKeys.self) + self.object = try container.decode(TestOperationObject.self, forKey: .object) + self.result = try container.decodeIfPresent(TestOperationResult.self, forKey: .result) + self.error = try container.decodeIfPresent(Bool.self, forKey: .error) + } + + /// Runs the operation and asserts its results meet the expectation. + func validateExecution( + client: SyncMongoClient, + database: SyncMongoDatabase?, + collection: SyncMongoCollection?, + session: SyncClientSession? + ) throws { + let target: TestOperationTarget + switch self.object { + case .client: + target = .client(client) + case .database: + guard let database = database else { + throw UserError.invalidArgumentError(message: "got database object but was not provided a database") + } + target = .database(database) + case .collection: + guard let collection = collection else { + throw UserError.invalidArgumentError(message: "got collection object but was not provided a collection") + } + target = .collection(collection) + case .gridfsbucket: + throw UserError.invalidArgumentError(message: "gridfs tests should be skipped") + } + + do { + let result = try self.operation.execute(on: target, session: session) + expect(self.error ?? false) + .to(beFalse(), description: "expected to fail but succeeded with result \(String(describing: result))") + if let expectedResult = self.result { + expect(result).to(equal(expectedResult)) + } + } catch { + expect(self.error ?? false).to(beTrue(), description: "expected no error, got \(error)") + } + } +} + +/// Object in which an operation should be executed on. +/// Not all target cases are supported by each operation. +enum TestOperationTarget { + /// Execute against the provided client. + case client(SyncMongoClient) + + /// Execute against the provided database. + case database(SyncMongoDatabase) + + /// Execute against the provided collection. + case collection(SyncMongoCollection) +} /// Protocol describing the behavior of a spec test "operation" protocol TestOperation: Decodable { /// Execute the operation given the context. - func execute( - client: SyncMongoClient, - database: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? - ) throws -> TestOperationResult? + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? } /// Wrapper around a `TestOperation.swift` allowing it to be decoded from a spec test. -struct AnyTestOperation: Decodable { +struct AnyTestOperation: Decodable, TestOperation { let op: TestOperation private enum CodingKeys: String, CodingKey { @@ -59,10 +138,34 @@ struct AnyTestOperation: Decodable { self.op = try container.decode(RenameCollection.self, forKey: .arguments) case "drop": self.op = DropCollection() + case "listDatabaseNames": + self.op = ListDatabaseNames() + case "listDatabases": + self.op = ListDatabases() + case "listDatabaseObjects": + self.op = ListMongoDatabases() + case "listIndexes": + self.op = ListIndexes() + case "listIndexNames": + self.op = ListIndexNames() + case "listCollections": + self.op = ListCollections() + case "listCollectionObjects": + self.op = ListMongoCollections() + case "listCollectionNames": + self.op = ListCollectionNames() + case "watch": + self.op = Watch() + case "mapReduce", "download_by_name", "findOne", "download": + self.op = NotImplemented(name: opName) default: throw UserError.logicError(message: "unsupported op name \(opName)") } } + + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + return try self.op.execute(on: target, session: session) + } } struct Aggregate: TestOperation { @@ -77,14 +180,13 @@ struct Aggregate: TestOperation { self.pipeline = try container.decode([Document].self, forKey: .pipeline) } - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? = nil - ) throws -> TestOperationResult? { - let cursor = try collection.aggregate(self.pipeline, options: self.options, session: session) - return TestOperationResult(from: cursor) + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to aggregate") + } + return try TestOperationResult( + from: collection.aggregate(self.pipeline, options: self.options, session: session) + ) } } @@ -100,35 +202,39 @@ struct CountDocuments: TestOperation { self.filter = try container.decode(Document.self, forKey: .filter) } - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? = nil - ) throws -> TestOperationResult? { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to count") + } return .int(try collection.countDocuments(self.filter, options: self.options, session: session)) } } struct Distinct: TestOperation { let fieldName: String + let filter: Document? let options: DistinctOptions - private enum CodingKeys: String, CodingKey { case fieldName } + private enum CodingKeys: String, CodingKey { case fieldName, filter } init(from decoder: Decoder) throws { self.options = try DistinctOptions(from: decoder) let container = try decoder.container(keyedBy: CodingKeys.self) self.fieldName = try container.decode(String.self, forKey: .fieldName) + self.filter = try container.decodeIfPresent(Document.self, forKey: .filter) } - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? = nil - ) throws -> TestOperationResult? { - return .array(try collection.distinct(fieldName: self.fieldName, options: self.options, session: session)) + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to distinct") + } + let result = try collection.distinct( + fieldName: self.fieldName, + filter: self.filter ?? [:], + options: self.options, + session: session + ) + return .array(result) } } @@ -144,13 +250,11 @@ struct Find: TestOperation { self.filter = try container.decode(Document.self, forKey: .filter) } - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? = nil - ) throws -> TestOperationResult? { - return TestOperationResult(from: try collection.find(self.filter, options: self.options, session: session)) + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to renameCollection") + } + return try TestOperationResult(from: collection.find(self.filter, options: self.options, session: session)) } } @@ -168,12 +272,11 @@ struct UpdateOne: TestOperation { self.update = try container.decode(Document.self, forKey: .update) } - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? = nil - ) throws -> TestOperationResult? { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to updateOne") + } + let result = try collection.updateOne( filter: self.filter, update: self.update, @@ -198,12 +301,11 @@ struct UpdateMany: TestOperation { self.update = try container.decode(Document.self, forKey: .update) } - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? = nil - ) throws -> TestOperationResult? { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to ") + } + let result = try collection.updateMany( filter: self.filter, update: self.update, @@ -226,12 +328,10 @@ struct DeleteMany: TestOperation { self.filter = try container.decode(Document.self, forKey: .filter) } - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? = nil - ) throws -> TestOperationResult? { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to deleteMany") + } let result = try collection.deleteMany(self.filter, options: self.options, session: session) return TestOperationResult(from: result) } @@ -249,12 +349,10 @@ struct DeleteOne: TestOperation { self.filter = try container.decode(Document.self, forKey: .filter) } - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? = nil - ) throws -> TestOperationResult? { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to deleteOne") + } let result = try collection.deleteOne(self.filter, options: self.options, session: session) return TestOperationResult(from: result) } @@ -263,12 +361,10 @@ struct DeleteOne: TestOperation { struct InsertOne: TestOperation { let document: Document - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session _: SyncClientSession? = nil - ) throws -> TestOperationResult? { + func execute(on target: TestOperationTarget, session _: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to insertOne") + } return TestOperationResult(from: try collection.insertOne(self.document)) } } @@ -277,17 +373,12 @@ struct InsertMany: TestOperation { let documents: [Document] let options: InsertManyOptions - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? = nil - ) throws -> TestOperationResult? { - return TestOperationResult(from: try collection.insertMany( - self.documents, - options: self.options, - session: session - )) + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to insertMany") + } + let result = try collection.insertMany(self.documents, options: self.options, session: session) + return TestOperationResult(from: result) } } @@ -357,12 +448,10 @@ struct BulkWrite: TestOperation { let requests: [WriteModel] let options: BulkWriteOptions - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? = nil - ) throws -> TestOperationResult? { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to bulk write") + } let result = try collection.bulkWrite(self.requests, options: self.options, session: session) return TestOperationResult(from: result) } @@ -382,12 +471,10 @@ struct FindOneAndUpdate: TestOperation { self.update = try container.decode(Document.self, forKey: .update) } - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? = nil - ) throws -> TestOperationResult? { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to findOneAndUpdate") + } let doc = try collection.findOneAndUpdate( filter: self.filter, update: self.update, @@ -410,12 +497,10 @@ struct FindOneAndDelete: TestOperation { self.filter = try container.decode(Document.self, forKey: .filter) } - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? = nil - ) throws -> TestOperationResult? { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to findOneAndDelete") + } let result = try collection.findOneAndDelete(self.filter, options: self.options, session: session) return TestOperationResult(from: result) } @@ -435,18 +520,17 @@ struct FindOneAndReplace: TestOperation { self.replacement = try container.decode(Document.self, forKey: .replacement) } - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? = nil - ) throws -> TestOperationResult? { - return TestOperationResult(from: try collection.findOneAndReplace( + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to findOneAndReplace") + } + let result = try collection.findOneAndReplace( filter: self.filter, replacement: self.replacement, options: self.options, session: session - )) + ) + return TestOperationResult(from: result) } } @@ -464,12 +548,10 @@ struct ReplaceOne: TestOperation { self.replacement = try container.decode(Document.self, forKey: .replacement) } - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session: SyncClientSession? = nil - ) throws -> TestOperationResult? { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to replaceOne") + } return TestOperationResult(from: try collection.replaceOne( filter: self.filter, replacement: self.replacement, @@ -482,27 +564,123 @@ struct ReplaceOne: TestOperation { struct RenameCollection: TestOperation { let to: String - func execute( - client: SyncMongoClient, - database: SyncMongoDatabase, - collection: SyncMongoCollection, - session _: SyncClientSession? = nil - ) throws -> TestOperationResult? { - let fromNamespace = database.name + "." + collection.name - let toNamespace = database.name + "." + self.to - let cmd: Document = ["renameCollection": .string(fromNamespace), "to": .string(toNamespace)] - return TestOperationResult(from: try client.db("admin").runCommand(cmd)) + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to renameCollection") + } + + let databaseName = collection.namespace.db + let cmd: Document = [ + "renameCollection": .string(databaseName + "." + collection.name), + "to": .string(databaseName + "." + self.to) + ] + return try TestOperationResult(from: collection._client.db("admin").runCommand(cmd, session: session)) } } struct DropCollection: TestOperation { - func execute( - client _: SyncMongoClient, - database _: SyncMongoDatabase, - collection: SyncMongoCollection, - session _: SyncClientSession? = nil - ) throws -> TestOperationResult? { + func execute(on target: TestOperationTarget, session _: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to dropCollection") + } try collection.drop() return nil } } + +struct ListDatabaseNames: TestOperation { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .client(client) = target else { + throw UserError.invalidArgumentError(message: "client not provided to listDatabaseNames") + } + return try .array(client.listDatabaseNames(session: session).map { .string($0) }) + } +} + +struct ListIndexes: TestOperation { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to listIndexes") + } + return try TestOperationResult(from: collection.listIndexes(session: session)) + } +} + +struct ListIndexNames: TestOperation { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to listIndexNames") + } + return try .array(collection.listIndexNames(session: session).map { .string($0) }) + } +} + +struct ListDatabases: TestOperation { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .client(client) = target else { + throw UserError.invalidArgumentError(message: "client not provided to listDatabases") + } + return try TestOperationResult(from: client.listDatabases(session: session)) + } +} + +struct ListMongoDatabases: TestOperation { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .client(client) = target else { + throw UserError.invalidArgumentError(message: "client not provided to listDatabases") + } + _ = try client.listMongoDatabases(session: session) + return nil + } +} + +struct ListCollections: TestOperation { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .database(database) = target else { + throw UserError.invalidArgumentError(message: "database not provided to listCollections") + } + return try TestOperationResult(from: database.listCollections(session: session)) + } +} + +struct ListMongoCollections: TestOperation { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .database(database) = target else { + throw UserError.invalidArgumentError(message: "database not provided to listCollectionObjects") + } + _ = try database.listMongoCollections(session: session) + return nil + } +} + +struct ListCollectionNames: TestOperation { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .database(database) = target else { + throw UserError.invalidArgumentError(message: "database not provided to listCollectionNames") + } + return try .array(database.listCollectionNames(session: session).map { .string($0) }) + } +} + +struct Watch: TestOperation { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + switch target { + case let .client(client): + _ = try client.watch(session: session) + case let .database(database): + _ = try database.watch(session: session) + case let .collection(collection): + _ = try collection.watch(session: session) + } + return nil + } +} + +/// Dummy `TestOperation` that can be used in place of an unimplemented one (e.g. findOne) +struct NotImplemented: TestOperation { + internal let name: String + + func execute(on _: TestOperationTarget, session _: SyncClientSession?) throws -> TestOperationResult? { + throw UserError.logicError(message: "\(self.name) not implemented in the driver, skip this test") + } +} diff --git a/Tests/MongoSwiftTests/SpecTestRunner/TestOperationResult.swift b/Tests/MongoSwiftTests/SpecTestRunner/TestOperationResult.swift index 7445b6257..ad5ab0db2 100644 --- a/Tests/MongoSwiftTests/SpecTestRunner/TestOperationResult.swift +++ b/Tests/MongoSwiftTests/SpecTestRunner/TestOperationResult.swift @@ -28,8 +28,16 @@ enum TestOperationResult: Decodable, Equatable { self = .bulkWrite(result.bulkResultValue) } - public init(from cursor: SyncMongoCursor) { - self = .array(cursor.map { .document($0) }) + public init(from cursor: SyncMongoCursor) throws { + let result = try cursor.map { BSON.document(try BSONEncoder().encode($0)) } + guard cursor.error == nil else { + throw cursor.error! + } + self = .array(result) + } + + public init(from array: [T]) throws { + self = try .array(array.map { .document(try BSONEncoder().encode($0)) }) } public init(from decoder: Decoder) throws { diff --git a/Tests/Scripts/add_json_files.rb b/Tests/Scripts/add_json_files.rb index 7989675ec..65332f81e 100644 --- a/Tests/Scripts/add_json_files.rb +++ b/Tests/Scripts/add_json_files.rb @@ -16,6 +16,7 @@ def make_reference(project, path) corpus = make_reference(project, "./Tests/Specs/bson-corpus") read_write_concern = make_reference(project, "./Tests/Specs/read-write-concern") retryable_writes = make_reference(project, "./Tests/Specs/retryable-writes") +retryable_reads = make_reference(project, "./Tests/Specs/retryable-reads") change_streams = make_reference(project, "./Tests/Specs/change-streams") dns_seedlist = make_reference(project, "./Tests/Specs/initial-dns-seedlist-discovery") auth = make_reference(project, "./Tests/Specs/auth") diff --git a/Tests/Specs/retryable-reads/tests/aggregate-merge.json b/Tests/Specs/retryable-reads/tests/aggregate-merge.json new file mode 100644 index 000000000..b401d741b --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/aggregate-merge.json @@ -0,0 +1,98 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.11" + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "tests": [ + { + "description": "Aggregate with $merge does not retry", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "object": "collection", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$merge": { + "into": "output-collection" + } + } + ] + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$merge": { + "into": "output-collection" + } + } + ] + }, + "command_name": "aggregate", + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/aggregate-serverErrors.json b/Tests/Specs/retryable-reads/tests/aggregate-serverErrors.json new file mode 100644 index 000000000..04208bc95 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/aggregate-serverErrors.json @@ -0,0 +1,1207 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "tests": [ + { + "description": "Aggregate succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "result": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "result": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "result": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "result": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "result": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "result": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "result": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "result": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "result": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "result": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "result": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/aggregate.json b/Tests/Specs/retryable-reads/tests/aggregate.json new file mode 100644 index 000000000..30a6e05e6 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/aggregate.json @@ -0,0 +1,405 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "tests": [ + { + "description": "Aggregate succeeds on first attempt", + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "result": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "result": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Aggregate with $out does not retry", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$out": "output-collection" + } + ] + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$out": "output-collection" + } + ] + }, + "command_name": "aggregate", + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/changeStreams-client.watch-serverErrors.json b/Tests/Specs/retryable-reads/tests/changeStreams-client.watch-serverErrors.json new file mode 100644 index 000000000..cf6c230ec --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/changeStreams-client.watch-serverErrors.json @@ -0,0 +1,738 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "tests": [ + { + "description": "client.watch succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "watch", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + }, + { + "description": "client.watch succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "watch", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + }, + { + "description": "client.watch succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "watch", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + }, + { + "description": "client.watch succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "watch", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + }, + { + "description": "client.watch succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "watch", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + }, + { + "description": "client.watch succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "watch", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + }, + { + "description": "client.watch succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "watch", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + }, + { + "description": "client.watch succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "watch", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + }, + { + "description": "client.watch succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "watch", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + }, + { + "description": "client.watch succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "watch", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + }, + { + "description": "client.watch succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "watch", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + }, + { + "description": "client.watch fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "watch", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + }, + { + "description": "client.watch fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "watch", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/changeStreams-client.watch.json b/Tests/Specs/retryable-reads/tests/changeStreams-client.watch.json new file mode 100644 index 000000000..9a2ccad09 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/changeStreams-client.watch.json @@ -0,0 +1,207 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + } + ], + "tests": [ + { + "description": "client.watch succeeds on first attempt", + "operations": [ + { + "name": "watch", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + }, + { + "description": "client.watch succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "watch", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + }, + { + "description": "client.watch fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "watch", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + }, + { + "description": "client.watch fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "watch", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "database_name": "admin" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/changeStreams-db.coll.watch-serverErrors.json b/Tests/Specs/retryable-reads/tests/changeStreams-db.coll.watch-serverErrors.json new file mode 100644 index 000000000..eb7df1e26 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/changeStreams-db.coll.watch-serverErrors.json @@ -0,0 +1,688 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "tests": [ + { + "description": "db.coll.watch succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "watch", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.coll.watch succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "watch", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.coll.watch succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "watch", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.coll.watch succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "watch", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.coll.watch succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "watch", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.coll.watch succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "watch", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.coll.watch succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "watch", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.coll.watch succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "watch", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.coll.watch succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "watch", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.coll.watch succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "watch", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.coll.watch succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "watch", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.coll.watch fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "watch", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.coll.watch fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "watch", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/changeStreams-db.coll.watch.json b/Tests/Specs/retryable-reads/tests/changeStreams-db.coll.watch.json new file mode 100644 index 000000000..3408c8423 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/changeStreams-db.coll.watch.json @@ -0,0 +1,195 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + } + ], + "tests": [ + { + "description": "db.coll.watch succeeds on first attempt", + "operations": [ + { + "name": "watch", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.coll.watch succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "watch", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.coll.watch fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "watch", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.coll.watch fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "watch", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/changeStreams-db.watch-serverErrors.json b/Tests/Specs/retryable-reads/tests/changeStreams-db.watch-serverErrors.json new file mode 100644 index 000000000..e070f56a0 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/changeStreams-db.watch-serverErrors.json @@ -0,0 +1,688 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "tests": [ + { + "description": "db.watch succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "watch", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.watch succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "watch", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.watch succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "watch", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.watch succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "watch", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.watch succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "watch", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.watch succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "watch", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.watch succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "watch", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.watch succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "watch", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.watch succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "watch", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.watch succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "watch", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.watch succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "watch", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.watch fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "watch", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.watch fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "watch", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/changeStreams-db.watch.json b/Tests/Specs/retryable-reads/tests/changeStreams-db.watch.json new file mode 100644 index 000000000..bec09c49b --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/changeStreams-db.watch.json @@ -0,0 +1,195 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + } + ], + "tests": [ + { + "description": "db.watch succeeds on first attempt", + "operations": [ + { + "name": "watch", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.watch succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "watch", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.watch fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "watch", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "db.watch fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "watch", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/count-serverErrors.json b/Tests/Specs/retryable-reads/tests/count-serverErrors.json new file mode 100644 index 000000000..839680fe5 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/count-serverErrors.json @@ -0,0 +1,585 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ], + "tests": [ + { + "description": "Count succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Count succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Count succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Count succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Count succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Count succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Count succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Count succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Count succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Count succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Count succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Count fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Count fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/count.json b/Tests/Specs/retryable-reads/tests/count.json new file mode 100644 index 000000000..0ccf4982b --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/count.json @@ -0,0 +1,178 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ], + "tests": [ + { + "description": "Count succeeds on first attempt", + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Count succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Count fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Count fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/countDocuments-serverErrors.json b/Tests/Specs/retryable-reads/tests/countDocuments-serverErrors.json new file mode 100644 index 000000000..f45eadfa0 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/countDocuments-serverErrors.json @@ -0,0 +1,910 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ], + "tests": [ + { + "description": "CountDocuments succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "CountDocuments succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "CountDocuments succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "CountDocuments succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "CountDocuments succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "CountDocuments succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "CountDocuments succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "CountDocuments succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "CountDocuments succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "CountDocuments succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "CountDocuments succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "CountDocuments fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "CountDocuments fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/countDocuments.json b/Tests/Specs/retryable-reads/tests/countDocuments.json new file mode 100644 index 000000000..b4ccf3668 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/countDocuments.json @@ -0,0 +1,256 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ], + "tests": [ + { + "description": "CountDocuments succeeds on first attempt", + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "CountDocuments succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "CountDocuments fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "CountDocuments fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/distinct-serverErrors.json b/Tests/Specs/retryable-reads/tests/distinct-serverErrors.json new file mode 100644 index 000000000..50fd6a550 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/distinct-serverErrors.json @@ -0,0 +1,837 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "tests": [ + { + "description": "Distinct succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "result": [ + 22, + 33 + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Distinct succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "result": [ + 22, + 33 + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Distinct succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "result": [ + 22, + 33 + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Distinct succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "result": [ + 22, + 33 + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Distinct succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "result": [ + 22, + 33 + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Distinct succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "result": [ + 22, + 33 + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Distinct succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "result": [ + 22, + 33 + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Distinct succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "result": [ + 22, + 33 + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Distinct succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "result": [ + 22, + 33 + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Distinct succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "result": [ + 22, + 33 + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Distinct succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "result": [ + 22, + 33 + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Distinct fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Distinct fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/distinct.json b/Tests/Specs/retryable-reads/tests/distinct.json new file mode 100644 index 000000000..b5885e27e --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/distinct.json @@ -0,0 +1,244 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "tests": [ + { + "description": "Distinct succeeds on first attempt", + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "result": [ + 22, + 33 + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Distinct succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "result": [ + 22, + 33 + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Distinct fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Distinct fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "distinct" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/estimatedDocumentCount-serverErrors.json b/Tests/Specs/retryable-reads/tests/estimatedDocumentCount-serverErrors.json new file mode 100644 index 000000000..1af21d1fe --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/estimatedDocumentCount-serverErrors.json @@ -0,0 +1,546 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ], + "tests": [ + { + "description": "EstimatedDocumentCount succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "EstimatedDocumentCount fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "EstimatedDocumentCount fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/estimatedDocumentCount.json b/Tests/Specs/retryable-reads/tests/estimatedDocumentCount.json new file mode 100644 index 000000000..8dfa15a2c --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/estimatedDocumentCount.json @@ -0,0 +1,166 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ], + "tests": [ + { + "description": "EstimatedDocumentCount succeeds on first attempt", + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "result": 2 + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "EstimatedDocumentCount fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "EstimatedDocumentCount fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "count": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/find-serverErrors.json b/Tests/Specs/retryable-reads/tests/find-serverErrors.json new file mode 100644 index 000000000..44ecf34d2 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/find-serverErrors.json @@ -0,0 +1,961 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ], + "tests": [ + { + "description": "Find succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "result": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "result": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "result": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "result": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "result": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "result": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "result": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "result": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "result": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "result": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "result": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/find.json b/Tests/Specs/retryable-reads/tests/find.json new file mode 100644 index 000000000..56479ff1d --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/find.json @@ -0,0 +1,347 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ], + "tests": [ + { + "description": "Find succeeds on first attempt", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "result": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find succeeds on second attempt with explicit clientOptions", + "clientOptions": { + "retryReads": true + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "result": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "result": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Find fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/findOne-serverErrors.json b/Tests/Specs/retryable-reads/tests/findOne-serverErrors.json new file mode 100644 index 000000000..b8229483d --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/findOne-serverErrors.json @@ -0,0 +1,731 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ], + "tests": [ + { + "description": "FindOne succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "x": 11 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "FindOne succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "x": 11 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "FindOne succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "x": 11 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "FindOne succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "x": 11 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "FindOne succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "x": 11 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "FindOne succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "x": 11 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "FindOne succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "x": 11 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "FindOne succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "x": 11 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "FindOne succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "x": 11 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "FindOne succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "x": 11 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "FindOne succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "x": 11 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "FindOne fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "FindOne fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/findOne.json b/Tests/Specs/retryable-reads/tests/findOne.json new file mode 100644 index 000000000..d296a9cdb --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/findOne.json @@ -0,0 +1,222 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ], + "tests": [ + { + "description": "FindOne succeeds on first attempt", + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "x": 11 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "FindOne succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "x": 11 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "FindOne fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "FindOne fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/gridfs-download-serverErrors.json b/Tests/Specs/retryable-reads/tests/gridfs-download-serverErrors.json new file mode 100644 index 000000000..84e50e370 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/gridfs-download-serverErrors.json @@ -0,0 +1,924 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "bucket_name": "fs", + "data": { + "fs.files": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "abc", + "metadata": {} + } + ], + "fs.chunks": [ + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000001" + }, + "n": 0, + "data": { + "$binary": { + "base64": "EQ==", + "subType": "00" + } + } + } + ] + }, + "tests": [ + { + "description": "Download succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Download succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Download succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Download succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Download succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Download succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Download succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Download succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Download succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Download succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Download succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Download fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Download fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/gridfs-download.json b/Tests/Specs/retryable-reads/tests/gridfs-download.json new file mode 100644 index 000000000..a5c5ef4d5 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/gridfs-download.json @@ -0,0 +1,269 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "bucket_name": "fs", + "data": { + "fs.files": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "abc", + "metadata": {} + } + ], + "fs.chunks": [ + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000001" + }, + "n": 0, + "data": { + "$binary": { + "base64": "EQ==", + "subType": "00" + } + } + } + ] + }, + "tests": [ + { + "description": "Download succeeds on first attempt", + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Download succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Download fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "Download fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "download", + "object": "gridfsbucket", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/gridfs-downloadByName-serverErrors.json b/Tests/Specs/retryable-reads/tests/gridfs-downloadByName-serverErrors.json new file mode 100644 index 000000000..de439ce4b --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/gridfs-downloadByName-serverErrors.json @@ -0,0 +1,848 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "bucket_name": "fs", + "data": { + "fs.files": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "abc", + "metadata": {} + } + ], + "fs.chunks": [ + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000001" + }, + "n": 0, + "data": { + "$binary": { + "base64": "EQ==", + "subType": "00" + } + } + } + ] + }, + "tests": [ + { + "description": "DownloadByName succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "DownloadByName succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "DownloadByName succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "DownloadByName succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "DownloadByName succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "DownloadByName succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "DownloadByName succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "DownloadByName succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "DownloadByName succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "DownloadByName succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "DownloadByName succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "DownloadByName fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "DownloadByName fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/gridfs-downloadByName.json b/Tests/Specs/retryable-reads/tests/gridfs-downloadByName.json new file mode 100644 index 000000000..0634a09bf --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/gridfs-downloadByName.json @@ -0,0 +1,249 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "bucket_name": "fs", + "data": { + "fs.files": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "abc", + "metadata": {} + } + ], + "fs.chunks": [ + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000001" + }, + "n": 0, + "data": { + "$binary": { + "base64": "EQ==", + "subType": "00" + } + } + } + ] + }, + "tests": [ + { + "description": "DownloadByName succeeds on first attempt", + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "DownloadByName succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.chunks", + "filter": { + "files_id": { + "$oid": "000000000000000000000001" + } + }, + "sort": { + "n": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "DownloadByName fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "DownloadByName fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "download_by_name", + "object": "gridfsbucket", + "arguments": { + "filename": "abc" + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listCollectionNames-serverErrors.json b/Tests/Specs/retryable-reads/tests/listCollectionNames-serverErrors.json new file mode 100644 index 000000000..27c13d630 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listCollectionNames-serverErrors.json @@ -0,0 +1,501 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListCollectionNames succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionNames succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionNames succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionNames succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionNames succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionNames succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionNames succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionNames succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionNames succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionNames succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionNames succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionNames fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionNames fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listCollectionNames.json b/Tests/Specs/retryable-reads/tests/listCollectionNames.json new file mode 100644 index 000000000..437fc36a4 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listCollectionNames.json @@ -0,0 +1,149 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListCollectionNames succeeds on first attempt", + "operations": [ + { + "name": "listCollectionNames", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionNames succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionNames fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionNames fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listCollectionNames", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listCollectionObjects-serverErrors.json b/Tests/Specs/retryable-reads/tests/listCollectionObjects-serverErrors.json new file mode 100644 index 000000000..3922713df --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listCollectionObjects-serverErrors.json @@ -0,0 +1,501 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListCollectionObjects succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionObjects succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionObjects succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionObjects succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionObjects succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionObjects succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionObjects succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionObjects succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionObjects succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionObjects succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionObjects succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionObjects fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionObjects fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listCollectionObjects.json b/Tests/Specs/retryable-reads/tests/listCollectionObjects.json new file mode 100644 index 000000000..1f537b743 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listCollectionObjects.json @@ -0,0 +1,149 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListCollectionObjects succeeds on first attempt", + "operations": [ + { + "name": "listCollectionObjects", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionObjects succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionObjects fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollectionObjects fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listCollectionObjects", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listCollections-serverErrors.json b/Tests/Specs/retryable-reads/tests/listCollections-serverErrors.json new file mode 100644 index 000000000..6972073b1 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listCollections-serverErrors.json @@ -0,0 +1,501 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListCollections succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollections succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollections succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollections succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollections succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollections succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollections succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollections succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollections succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollections succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollections succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollections fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollections fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listCollections.json b/Tests/Specs/retryable-reads/tests/listCollections.json new file mode 100644 index 000000000..a6b452e64 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listCollections.json @@ -0,0 +1,149 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListCollections succeeds on first attempt", + "operations": [ + { + "name": "listCollections", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollections succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollections fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + }, + { + "description": "ListCollections fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listCollections", + "object": "database", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listDatabaseNames-serverErrors.json b/Tests/Specs/retryable-reads/tests/listDatabaseNames-serverErrors.json new file mode 100644 index 000000000..11faf58bf --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listDatabaseNames-serverErrors.json @@ -0,0 +1,501 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListDatabaseNames succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseNames succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseNames succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseNames succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseNames succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseNames succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseNames succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseNames succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseNames succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseNames succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseNames succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseNames fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseNames fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listDatabaseNames.json b/Tests/Specs/retryable-reads/tests/listDatabaseNames.json new file mode 100644 index 000000000..b35f7ab18 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listDatabaseNames.json @@ -0,0 +1,149 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListDatabaseNames succeeds on first attempt", + "operations": [ + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseNames succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseNames fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseNames fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listDatabaseNames", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listDatabaseObjects-serverErrors.json b/Tests/Specs/retryable-reads/tests/listDatabaseObjects-serverErrors.json new file mode 100644 index 000000000..38082f2e2 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listDatabaseObjects-serverErrors.json @@ -0,0 +1,501 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListDatabaseObjects succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseObjects fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseObjects fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listDatabaseObjects.json b/Tests/Specs/retryable-reads/tests/listDatabaseObjects.json new file mode 100644 index 000000000..cbd2c6763 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listDatabaseObjects.json @@ -0,0 +1,149 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListDatabaseObjects succeeds on first attempt", + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseObjects succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseObjects fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabaseObjects fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listDatabaseObjects", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listDatabases-serverErrors.json b/Tests/Specs/retryable-reads/tests/listDatabases-serverErrors.json new file mode 100644 index 000000000..4047f749f --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listDatabases-serverErrors.json @@ -0,0 +1,501 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListDatabases succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabases succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabases succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabases succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabases succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabases succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabases succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabases succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabases succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabases succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabases succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabases fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabases fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listDatabases.json b/Tests/Specs/retryable-reads/tests/listDatabases.json new file mode 100644 index 000000000..3cb8bbd08 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listDatabases.json @@ -0,0 +1,149 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListDatabases succeeds on first attempt", + "operations": [ + { + "name": "listDatabases", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabases succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabases fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + }, + { + "description": "ListDatabases fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listDatabases", + "object": "client", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + }, + { + "command_started_event": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listIndexNames-serverErrors.json b/Tests/Specs/retryable-reads/tests/listIndexNames-serverErrors.json new file mode 100644 index 000000000..1a9ba83bc --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listIndexNames-serverErrors.json @@ -0,0 +1,526 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListIndexNames succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listIndexNames.json b/Tests/Specs/retryable-reads/tests/listIndexNames.json new file mode 100644 index 000000000..912c70601 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listIndexNames.json @@ -0,0 +1,155 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListIndexNames succeeds on first attempt", + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexNames fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listIndexNames", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listIndexes-serverErrors.json b/Tests/Specs/retryable-reads/tests/listIndexes-serverErrors.json new file mode 100644 index 000000000..16b61d535 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listIndexes-serverErrors.json @@ -0,0 +1,526 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListIndexes succeeds after InterruptedAtShutdown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 11600 + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexes succeeds after InterruptedDueToReplStateChange", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 11602 + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexes succeeds after NotMaster", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexes succeeds after NotMasterNoSlaveOk", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 13435 + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexes succeeds after NotMasterOrSecondary", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 13436 + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexes succeeds after PrimarySteppedDown", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 189 + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexes succeeds after ShutdownInProgress", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 91 + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexes succeeds after HostNotFound", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 7 + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexes succeeds after HostUnreachable", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 6 + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexes succeeds after NetworkTimeout", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 89 + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexes succeeds after SocketException", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 9001 + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexes fails after two NotMaster errors", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexes fails after NotMaster when retryReads is false", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 10107 + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/listIndexes.json b/Tests/Specs/retryable-reads/tests/listIndexes.json new file mode 100644 index 000000000..f460ea768 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/listIndexes.json @@ -0,0 +1,155 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [], + "tests": [ + { + "description": "ListIndexes succeeds on first attempt", + "operations": [ + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexes succeeds on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexes fails on first attempt", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "ListIndexes fails on second attempt", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "listIndexes", + "object": "collection", + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + }, + { + "command_started_event": { + "command": { + "listIndexes": "coll" + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} diff --git a/Tests/Specs/retryable-reads/tests/mapReduce.json b/Tests/Specs/retryable-reads/tests/mapReduce.json new file mode 100644 index 000000000..9dc7a56f3 --- /dev/null +++ b/Tests/Specs/retryable-reads/tests/mapReduce.json @@ -0,0 +1,187 @@ +{ + "runOn": [ + { + "minServerVersion": "4.0", + "topology": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topology": [ + "sharded" + ] + } + ], + "database_name": "retryable-reads-tests", + "collection_name": "coll", + "data": [ + { + "_id": 1, + "x": 0 + }, + { + "_id": 2, + "x": 1 + }, + { + "_id": 3, + "x": 2 + } + ], + "tests": [ + { + "description": "MapReduce succeeds with retry on", + "operations": [ + { + "name": "mapReduce", + "object": "collection", + "arguments": { + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "result": [ + { + "_id": 0, + "value": 6 + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "mapReduce": "coll", + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "MapReduce fails with retry on", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "mapReduce" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "mapReduce", + "object": "collection", + "arguments": { + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "mapReduce": "coll", + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + }, + { + "description": "MapReduce fails with retry off", + "clientOptions": { + "retryReads": false + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "mapReduce" + ], + "closeConnection": true + } + }, + "operations": [ + { + "name": "mapReduce", + "object": "collection", + "arguments": { + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "error": true + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "mapReduce": "coll", + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "database_name": "retryable-reads-tests" + } + } + ] + } + ] +} From 0028018588afa407b69ae28d7960e5e551b48747 Mon Sep 17 00:00:00 2001 From: Patrick Freed Date: Tue, 22 Oct 2019 17:51:16 -0400 Subject: [PATCH 2/6] fix linuxmain --- Tests/LinuxMain.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 8d530d621..75529f44d 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -1,4 +1,4 @@ -// Generated using Sourcery 0.16.2 — https://github.com/krzysztofzablocki/Sourcery +// Generated using Sourcery 0.16.1 — https://github.com/krzysztofzablocki/Sourcery // DO NOT EDIT From d1bfb542b314498338030cbe4f00249eeb7af884 Mon Sep 17 00:00:00 2001 From: Patrick Freed Date: Tue, 22 Oct 2019 18:24:51 -0400 Subject: [PATCH 3/6] minor updates --- Sources/MongoSwift/MongoClient.swift | 4 ++-- Tests/MongoSwiftTests/RetryableReadsTests.swift | 6 +++--- Tests/MongoSwiftTests/SpecTestRunner/SpecTest.swift | 9 ++++----- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Sources/MongoSwift/MongoClient.swift b/Sources/MongoSwift/MongoClient.swift index 069fd8152..8b5ec99bf 100644 --- a/Sources/MongoSwift/MongoClient.swift +++ b/Sources/MongoSwift/MongoClient.swift @@ -32,10 +32,10 @@ public struct ClientOptions: CodingStrategyProvider, Decodable { /// Specifies a ReadPreference to use for the client. public var readPreference: ReadPreference? = nil - /// Determines whether the client should retry supported read operations. + /// Determines whether the client should retry supported read operations (on by default). public var retryReads: Bool? - /// Determines whether the client should retry supported write operations. + /// Determines whether the client should retry supported write operations (on by default). public var retryWrites: Bool? /** diff --git a/Tests/MongoSwiftTests/RetryableReadsTests.swift b/Tests/MongoSwiftTests/RetryableReadsTests.swift index 3a644602c..d66461c5e 100644 --- a/Tests/MongoSwiftTests/RetryableReadsTests.swift +++ b/Tests/MongoSwiftTests/RetryableReadsTests.swift @@ -53,10 +53,10 @@ final class RetryableReadsTests: MongoSwiftTestCase, FailPointConfigured { func testRetryableReads() throws { let skippedTestKeywords = [ "findOne", - "estimatedDocumentCount", - "changeStream", // TODO: Unskip this test once CDRIVER-3405 is resolved + "changeStream", // TODO: SWIFT-648: Unskip this test "gridfs", - "countDocuments", + "countDocuments", // TODO: SWIFT-133: Unskip this test + "estimatedDocumentCount", // TODO: SWIFT-133: Unskip this test "mapReduce" ] diff --git a/Tests/MongoSwiftTests/SpecTestRunner/SpecTest.swift b/Tests/MongoSwiftTests/SpecTestRunner/SpecTest.swift index 5ac16603b..6918a7032 100644 --- a/Tests/MongoSwiftTests/SpecTestRunner/SpecTest.swift +++ b/Tests/MongoSwiftTests/SpecTestRunner/SpecTest.swift @@ -397,7 +397,7 @@ extension SpecTestFile { print("Skipping test \(test.description)") return } - try test.run(parent: parent, dbName: self.databaseName, collName: self.collectionName, session: nil) + try test.run(parent: parent, dbName: self.databaseName, collName: self.collectionName) } } } @@ -415,14 +415,14 @@ internal protocol SpecTest: Decodable { /// This field has no effect for non-sharded topologies. var useMultipleMongoses: Bool? { get } - /// Reason why this test should be skipped. + /// Reason why this test should be skipped, if applicable. var skipReason: String? { get } /// The optional fail point to configure before running this test. /// This option and useMultipleMongoses: true are mutually exclusive. var failPoint: FailPoint? { get } - /// + /// Descriptions of the operations to be run and their expected outcomes. var operations: [TestOperationDescription] { get } /// List of expected CommandStartedEvents. @@ -434,8 +434,7 @@ extension SpecTest { internal func run( parent: FailPointConfigured, dbName: String, - collName: String?, - session _: SyncClientSession? + collName: String? ) throws { guard self.skipReason == nil else { print("Skipping test for reason: \(self.skipReason!)") From 4c206252e0d6317c349caa4139e8b2eefc2996c3 Mon Sep 17 00:00:00 2001 From: Patrick Freed Date: Wed, 23 Oct 2019 12:31:57 -0400 Subject: [PATCH 4/6] unskip new count methods --- Tests/MongoSwiftTests/RetryableReadsTests.swift | 4 ++-- .../SpecTestRunner/TestOperation.swift | 13 ++++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Tests/MongoSwiftTests/RetryableReadsTests.swift b/Tests/MongoSwiftTests/RetryableReadsTests.swift index d66461c5e..8ead457e7 100644 --- a/Tests/MongoSwiftTests/RetryableReadsTests.swift +++ b/Tests/MongoSwiftTests/RetryableReadsTests.swift @@ -55,8 +55,8 @@ final class RetryableReadsTests: MongoSwiftTestCase, FailPointConfigured { "findOne", "changeStream", // TODO: SWIFT-648: Unskip this test "gridfs", - "countDocuments", // TODO: SWIFT-133: Unskip this test - "estimatedDocumentCount", // TODO: SWIFT-133: Unskip this test + "count.", + "count-", "mapReduce" ] diff --git a/Tests/MongoSwiftTests/SpecTestRunner/TestOperation.swift b/Tests/MongoSwiftTests/SpecTestRunner/TestOperation.swift index 65b95931e..92ac1191b 100644 --- a/Tests/MongoSwiftTests/SpecTestRunner/TestOperation.swift +++ b/Tests/MongoSwiftTests/SpecTestRunner/TestOperation.swift @@ -108,6 +108,8 @@ struct AnyTestOperation: Decodable, TestOperation { self.op = try container.decode(Aggregate.self, forKey: .arguments) case "countDocuments": self.op = try container.decode(CountDocuments.self, forKey: .arguments) + case "estimatedDocumentCount": + self.op = EstimatedDocumentCount() case "distinct": self.op = try container.decode(Distinct.self, forKey: .arguments) case "find": @@ -156,7 +158,7 @@ struct AnyTestOperation: Decodable, TestOperation { self.op = ListCollectionNames() case "watch": self.op = Watch() - case "mapReduce", "download_by_name", "findOne", "download": + case "mapReduce", "download_by_name", "findOne", "download", "count": self.op = NotImplemented(name: opName) default: throw UserError.logicError(message: "unsupported op name \(opName)") @@ -676,6 +678,15 @@ struct Watch: TestOperation { } } +struct EstimatedDocumentCount: TestOperation { + func execute(on target: TestOperationTarget, session: SyncClientSession?) throws -> TestOperationResult? { + guard case let .collection(collection) = target else { + throw UserError.invalidArgumentError(message: "collection not provided to estimatedDocumentCount") + } + return try .int(collection.estimatedDocumentCount(session: session)) + } +} + /// Dummy `TestOperation` that can be used in place of an unimplemented one (e.g. findOne) struct NotImplemented: TestOperation { internal let name: String From 62dfd08761fb3ab1e216492fc05b335dddbbbb93 Mon Sep 17 00:00:00 2001 From: Patrick Freed Date: Thu, 24 Oct 2019 11:14:39 -0400 Subject: [PATCH 5/6] update add_json_files.rb --- Tests/Scripts/add_json_files.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Scripts/add_json_files.rb b/Tests/Scripts/add_json_files.rb index 65332f81e..3beec621e 100644 --- a/Tests/Scripts/add_json_files.rb +++ b/Tests/Scripts/add_json_files.rb @@ -21,6 +21,6 @@ def make_reference(project, path) dns_seedlist = make_reference(project, "./Tests/Specs/initial-dns-seedlist-discovery") auth = make_reference(project, "./Tests/Specs/auth") -tests_target.add_resources([crud, cm, corpus, read_write_concern, retryable_writes, change_streams, dns_seedlist, auth]) +tests_target.add_resources([crud, cm, corpus, read_write_concern, retryable_writes, retryable_reads, change_streams, dns_seedlist, auth]) project.save From cb0a9bf1d5d77595fdde0f7931be9535cda86ba5 Mon Sep 17 00:00:00 2001 From: Patrick Freed Date: Fri, 25 Oct 2019 16:18:30 -0400 Subject: [PATCH 6/6] add todo --- Tests/MongoSwiftTests/RetryableReadsTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/MongoSwiftTests/RetryableReadsTests.swift b/Tests/MongoSwiftTests/RetryableReadsTests.swift index 8ead457e7..ce1512f58 100644 --- a/Tests/MongoSwiftTests/RetryableReadsTests.swift +++ b/Tests/MongoSwiftTests/RetryableReadsTests.swift @@ -52,7 +52,7 @@ final class RetryableReadsTests: MongoSwiftTestCase, FailPointConfigured { func testRetryableReads() throws { let skippedTestKeywords = [ - "findOne", + "findOne", // TODO: SWIFT-643: Unskip this test "changeStream", // TODO: SWIFT-648: Unskip this test "gridfs", "count.",