From 919fceb5931d2e3e4502b5b0a851ae14c1221124 Mon Sep 17 00:00:00 2001 From: BorisDog Date: Wed, 7 May 2025 12:07:15 -0700 Subject: [PATCH 01/24] CSHARP-5583: Update README.md (#1681) --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e2495368ff1..a4b7f919ddc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,14 @@ MongoDB C# Driver ================= -You can get the latest stable release from the [official Nuget.org feed](https://www.nuget.org/packages/MongoDB.Driver) or from our [github releases page](https://github.com/mongodb/mongo-csharp-driver/releases). +[![MongoDB.Driver](https://img.shields.io/nuget/v/MongoDB.Driver.svg)](https://www.nuget.org/packages/MongoDB.Driver/) +[![Documentation](https://img.shields.io/badge/docs-docfx-blue.svg)](https://mongodb.github.io/mongo-csharp-driver/3.4.0/api/index.html) +[![Documentation](https://img.shields.io/badge/docs-mongo-green.svg)](https://www.mongodb.com/docs/drivers/csharp/current/) +[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/mongodb/mongo-csharp-driver/blob/main/LICENSE.md) + +The official MongoDB .NET/C# driver. + +The MongoDB .NET/C# driver follows [semantic versioning](https://semver.org/) since v3.0.0 of its releases. Getting Started --------------- @@ -62,7 +69,8 @@ foreach(var person in list) Documentation ------------- * [MongoDB](https://www.mongodb.com/docs) -* [Documentation](https://www.mongodb.com/docs/drivers/csharp/current/) +* [.NET/C# Driver](https://www.mongodb.com/docs/drivers/csharp/current/) +* [API Reference](https://mongodb.github.io/mongo-csharp-driver/3.4.0/api/index.html) Questions/Bug Reports --------------------- From 6b3835ca63dda2cd02826a0131da5a3116c7024d Mon Sep 17 00:00:00 2001 From: Ahmad Shah <112424973+MAhmadShah@users.noreply.github.com> Date: Thu, 8 May 2025 12:53:58 -0400 Subject: [PATCH 02/24] DEVPROD-17449: Replace perf.send with new direct end point for performance data submission (#1689) --- evergreen/evergreen.yml | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/evergreen/evergreen.yml b/evergreen/evergreen.yml index 572d312625f..8cbf5c9ba40 100644 --- a/evergreen/evergreen.yml +++ b/evergreen/evergreen.yml @@ -476,10 +476,39 @@ functions: script: | ${PREPARE_SHELL} MONGODB_URI="${MONGODB_URI}" ../../evergreen/run-perf-tests.sh - - command: perf.send + - command: shell.exec params: - file: mongo-csharp-driver/benchmarks/MongoDB.Driver.Benchmarks/Benchmark.Artifacts/results/evergreen-results.json + script: | + # We use the requester expansion to determine whether the data is from a mainline evergreen run or not + if [ "${requester}" == "commit" ]; then + is_mainline=true + else + is_mainline=false + fi + + # We parse the username out of the order_id as patches append that in and SPS does not need that information + parsed_order_id=$(echo "${revision_order_id}" | awk -F'_' '{print $NF}') + + # Submit the performance data to the SPS endpoint + response=$(curl -s -w "\nHTTP_STATUS:%{http_code}" -X 'POST' \ + "https://performance-monitoring-api.corp.mongodb.com/raw_perf_results/cedar_report?project=${project_id}&version=${version_id}&variant=${build_variant}&order=$parsed_order_id&task_name=${task_name}&task_id=${task_id}&execution=${execution}&mainline=$is_mainline" \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -d @mongo-csharp-driver/benchmarks/MongoDB.Driver.Benchmarks/Benchmark.Artifacts/results/evergreen-results.json) + + http_status=$(echo "$response" | grep "HTTP_STATUS" | awk -F':' '{print $2}') + response_body=$(echo "$response" | sed '/HTTP_STATUS/d') + + # We want to throw an error if the data was not successfully submitted + if [ "$http_status" -ne 200 ]; then + echo "Error: Received HTTP status $http_status" + echo "Response Body: $response_body" + exit 1 + fi + echo "Response Body: $response_body" + echo "HTTP Status: $http_status" + assume-ec2-role: - command: ec2.assume_role params: From 79d97edcc48263211f7a86e1322120a6c77e7555 Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Fri, 9 May 2025 16:06:20 -0700 Subject: [PATCH 03/24] CSHARP-4918: Release notes automation (#1680) --- .github/workflows/pr.yml | 30 ++++++++++++++++++++++++++++++ evergreen/release-notes.py | 7 ++++--- 2 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/pr.yml diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 00000000000..0faa7e70dbe --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,30 @@ +name: Pull Request validation + +on: + pull_request: + types: + - opened + - reopened + - edited + - labeled + - unlabeled + +jobs: + pull-request-validation: + name: Pull Request validation. + runs-on: ubuntu-latest + steps: + - name: Pull Request should have a label assigned. + if: ${{ always() && github.event.pull_request.labels[0] == null }} + run: | + exit 1 + + - name: Title should start with a Jira ticket. + if: ${{ always() && !(startsWith(github.event.pull_request.title, 'CSHARP-')) }} + run: | + exit 1 + + - name: Title should not end with period or ellipses. + if: ${{ always() && (endsWith(github.event.pull_request.title, '.') || endsWith(github.event.pull_request.title, '…')) }} + run: | + exit 1 diff --git a/evergreen/release-notes.py b/evergreen/release-notes.py index 5d5d5367a10..ff388d8da5d 100644 --- a/evergreen/release-notes.py +++ b/evergreen/release-notes.py @@ -37,7 +37,7 @@ def load_config(opts): def mapPullRequest(pullRequest, opts): - title = pullRequest["title"] + title = pullRequest["title"].encode('ascii', 'backslashreplace').decode().replace("<", "\<") for regex in opts.template["autoformat"]: title = re.sub(regex["match"], regex["replace"], title) @@ -91,7 +91,9 @@ def load_pull_requests(opts): github_api_base_url=opts.github_api_base_url, repo=opts.repo, commit_sha=commit["sha"]) - pullrequests = requests.get(pullrequests_url, headers=opts.github_headers).json() + pullrequests_response = requests.get(pullrequests_url, headers=opts.github_headers) + pullrequests_response.raise_for_status() + pullrequests = pullrequests_response.json() for pullrequest in pullrequests: mapped = mapPullRequest(pullrequest, opts) if is_in_section(mapped, ignore_section): @@ -140,7 +142,6 @@ def publish_release_notes(opts, title, content): print("Publishing release notes...") url = '{github_api_base_url}{repo}/releases/tags/{tag}'.format(github_api_base_url=opts.github_api_base_url, repo=opts.repo, tag=opts.version_tag) response = requests.get(url, headers=opts.github_headers) - response.raise_for_status() if response.status_code != 404: raise SystemExit("Release with the tag already exists") From 1713173bbfd2efe4032177b728c5b499c54c433d Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Fri, 16 May 2025 13:11:16 -0700 Subject: [PATCH 04/24] CSHARP-5591: Configure MongoDB.Driver.Encryption.Tests to run on net472 target (#1692) --- build.cake | 6 ------ build.ps1 | 6 ++---- build.sh | 5 ++--- .../AstrolabeWorkloadExecutor.csproj | 4 ---- .../AtlasConnectivity.Tests.csproj | 4 ---- tests/BuildProps/Tests.Build.props | 1 + .../MongoDB.Bson.TestHelpers.csproj | 4 ---- tests/MongoDB.Bson.Tests/MongoDB.Bson.Tests.csproj | 8 ++------ .../MongoDB.Driver.Encryption.Tests.csproj | 10 +++------- .../MongoDB.Driver.Encryption.Tests/xunit.runner.json | 8 ++++---- .../MongoDB.Driver.Examples.csproj | 4 ---- .../MongoDB.Driver.TestHelpers.csproj | 1 - tests/MongoDB.Driver.Tests/MongoDB.Driver.Tests.csproj | 4 ---- 13 files changed, 14 insertions(+), 51 deletions(-) diff --git a/build.cake b/build.cake index d80fe9bea0d..9c50bb96ffe 100644 --- a/build.cake +++ b/build.cake @@ -117,12 +117,6 @@ Task("Test") items: GetFiles("./**/*.Tests.csproj").Where(name => !name.ToString().Contains("Atlas")), action: (BuildConfig buildConfig, Path testProject) => { - if (Environment.GetEnvironmentVariable("MONGODB_API_VERSION") != null && - testProject.ToString().Contains("Legacy")) - { - return; // Legacy tests are exempt from Version API testing - } - var mongoX509ClientCertificatePath = Environment.GetEnvironmentVariable("MONGO_X509_CLIENT_CERTIFICATE_PATH"); if (mongoX509ClientCertificatePath != null) { diff --git a/build.ps1 b/build.ps1 index c4eb3e1ae7d..cc4a4d4aa80 100644 --- a/build.ps1 +++ b/build.ps1 @@ -89,17 +89,16 @@ if($FoundDotNetCliVersion -ne $DotNetVersion) { New-Item -Path $InstallPath -ItemType Directory -Force | Out-Null; } - # N.B. We explicitly install .NET Core 2.1 and 3.1 because .NET 5.0 SDK can build those TFMs + # N.B. We explicitly install .NET Core 3.1 because .NET 5.0 SDK can build those TFMs # but will silently upgrade to a more recent runtime to execute tests if the desired runtime # isn't available. For example, `dotnet run --framework netcoreapp3.0` will silently run # on .NET 5.0 if .NET Core 3.0 and 3.1 aren't installed. - # This solution is admittedly hacky as .NET Core 2.1 and 3.1 won't be installed if + # This solution is admittedly hacky as .NET Core 3.1 won't be installed if # $DOTNET_VERSION matches $DOTNET_INSTALLED_VERSION, but it minimizes the changes required # to install required dependencies on Evergreen. if ($IsMacOS -or $IsLinux) { $ScriptPath = Join-Path $InstallPath 'dotnet-install.sh' (New-Object System.Net.WebClient).DownloadFile($DotNetUnixInstallerUri, $ScriptPath); - & bash $ScriptPath --install-dir "$InstallPath" --channel 2.1 --no-path & bash $ScriptPath --install-dir "$InstallPath" --channel 3.1 --no-path & bash $ScriptPath --install-dir "$InstallPath" --channel 5.0 --no-path & bash $ScriptPath --install-dir "$InstallPath" --channel 6.0 --no-path @@ -111,7 +110,6 @@ if($FoundDotNetCliVersion -ne $DotNetVersion) { else { $ScriptPath = Join-Path $InstallPath 'dotnet-install.ps1' (New-Object System.Net.WebClient).DownloadFile($DotNetInstallerUri, $ScriptPath); - & $ScriptPath -Channel 2.1 -InstallDir $InstallPath; & $ScriptPath -Channel 3.1 -InstallDir $InstallPath; & $ScriptPath -Channel 5.0 -InstallDir $InstallPath; & $ScriptPath -Channel 6.0 -InstallDir $InstallPath; diff --git a/build.sh b/build.sh index cba9c4531b4..a3ce5a72b9f 100755 --- a/build.sh +++ b/build.sh @@ -33,18 +33,17 @@ if [ "$DOTNET_VERSION" != "$DOTNET_INSTALLED_VERSION" ]; then mkdir "$SCRIPT_DIR/.dotnet" fi curl -Lfo "$SCRIPT_DIR/.dotnet/dotnet-install.sh" https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.sh - # N.B. We explicitly install .NET Core 2.1 and 3.1 because .NET 6.0 SDK can build those TFMs + # N.B. We explicitly install .NET Core 3.1 because .NET 6.0 SDK can build those TFMs # but will silently upgrade to a more recent runtime to execute tests if the desired runtime # isn't available. For example, `dotnet run --framework netcoreapp3.0` will silently run # on .NET 6.0 if .NET Core 3.0 and 3.1 aren't installed. - # This solution is admittedly hacky as .NET Core 2.1 and 3.1 won't be installed if + # This solution is admittedly hacky as .NET Core 3.1 won't be installed if # $DOTNET_VERSION matches $DOTNET_INSTALLED_VERSION, but it minimizes the changes required # to install required dependencies on Evergreen. # Since ARM64 support was first added in .NET 6.0, the following commands will install: # | CPU | 2.1 | 3.1 | Latest | # | x64 | x64 | x64 | x64 | # | arm64 | x64 | x64 | arm64 | - bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --channel 2.1 --architecture x64 --install-dir .dotnet --no-path bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --channel 3.1 --architecture x64 --install-dir .dotnet --no-path bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --channel 5.0 --architecture x64 --install-dir .dotnet --no-path bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --channel 6.0 --install-dir .dotnet --no-path diff --git a/tests/AstrolabeWorkloadExecutor/AstrolabeWorkloadExecutor.csproj b/tests/AstrolabeWorkloadExecutor/AstrolabeWorkloadExecutor.csproj index cd2dfe8d18c..da78dc4945f 100644 --- a/tests/AstrolabeWorkloadExecutor/AstrolabeWorkloadExecutor.csproj +++ b/tests/AstrolabeWorkloadExecutor/AstrolabeWorkloadExecutor.csproj @@ -17,10 +17,6 @@ Astrolabe workload executor. - - - - diff --git a/tests/AtlasConnectivity.Tests/AtlasConnectivity.Tests.csproj b/tests/AtlasConnectivity.Tests/AtlasConnectivity.Tests.csproj index 46c5d10a98d..f5ef150c64b 100644 --- a/tests/AtlasConnectivity.Tests/AtlasConnectivity.Tests.csproj +++ b/tests/AtlasConnectivity.Tests/AtlasConnectivity.Tests.csproj @@ -11,10 +11,6 @@ Atlas connectivity tests. - - - - 1701;1702; diff --git a/tests/BuildProps/Tests.Build.props b/tests/BuildProps/Tests.Build.props index a14112ae334..20c57ed0aba 100644 --- a/tests/BuildProps/Tests.Build.props +++ b/tests/BuildProps/Tests.Build.props @@ -43,6 +43,7 @@ + diff --git a/tests/MongoDB.Bson.TestHelpers/MongoDB.Bson.TestHelpers.csproj b/tests/MongoDB.Bson.TestHelpers/MongoDB.Bson.TestHelpers.csproj index 94f37f02a4b..00789c4be93 100644 --- a/tests/MongoDB.Bson.TestHelpers/MongoDB.Bson.TestHelpers.csproj +++ b/tests/MongoDB.Bson.TestHelpers/MongoDB.Bson.TestHelpers.csproj @@ -11,10 +11,6 @@ Helper classes applicable to test projects that reference MongoDB.Bson. - - - - diff --git a/tests/MongoDB.Bson.Tests/MongoDB.Bson.Tests.csproj b/tests/MongoDB.Bson.Tests/MongoDB.Bson.Tests.csproj index bdf1a1d2d06..ee5cfeb07f8 100644 --- a/tests/MongoDB.Bson.Tests/MongoDB.Bson.Tests.csproj +++ b/tests/MongoDB.Bson.Tests/MongoDB.Bson.Tests.csproj @@ -11,10 +11,6 @@ MongoDB.Bson tests. - - - - 1701;1702; @@ -31,10 +27,10 @@ - + - + diff --git a/tests/MongoDB.Driver.Encryption.Tests/MongoDB.Driver.Encryption.Tests.csproj b/tests/MongoDB.Driver.Encryption.Tests/MongoDB.Driver.Encryption.Tests.csproj index 42e6b24a7a1..2634984884a 100644 --- a/tests/MongoDB.Driver.Encryption.Tests/MongoDB.Driver.Encryption.Tests.csproj +++ b/tests/MongoDB.Driver.Encryption.Tests/MongoDB.Driver.Encryption.Tests.csproj @@ -1,11 +1,7 @@  - + - net472;netcoreapp3.1 - netcoreapp3.1;net6.0 - - AnyCPU false true ..\..\MongoDB.Driver.snk @@ -21,8 +17,8 @@ - - PreserveNewest + + Always diff --git a/tests/MongoDB.Driver.Encryption.Tests/xunit.runner.json b/tests/MongoDB.Driver.Encryption.Tests/xunit.runner.json index 991c5d665a4..df84b2cfa98 100644 --- a/tests/MongoDB.Driver.Encryption.Tests/xunit.runner.json +++ b/tests/MongoDB.Driver.Encryption.Tests/xunit.runner.json @@ -1,6 +1,6 @@ { - "$schema": "https://xunit.github.io/schema/current/xunit.runner.schema.json", - - "appDomain": "denied", - "shadowCopy": false + "longRunningTestSeconds": 10, + "parallelizeAssembly": false, + "parallelizeTestCollections": false, + "shadowCopy": false } diff --git a/tests/MongoDB.Driver.Examples/MongoDB.Driver.Examples.csproj b/tests/MongoDB.Driver.Examples/MongoDB.Driver.Examples.csproj index 3efd3ca9de4..c993cf89187 100644 --- a/tests/MongoDB.Driver.Examples/MongoDB.Driver.Examples.csproj +++ b/tests/MongoDB.Driver.Examples/MongoDB.Driver.Examples.csproj @@ -11,10 +11,6 @@ MongoDB.Driver examples. - - - - diff --git a/tests/MongoDB.Driver.TestHelpers/MongoDB.Driver.TestHelpers.csproj b/tests/MongoDB.Driver.TestHelpers/MongoDB.Driver.TestHelpers.csproj index 491068dd8bd..096fcb8bc0b 100644 --- a/tests/MongoDB.Driver.TestHelpers/MongoDB.Driver.TestHelpers.csproj +++ b/tests/MongoDB.Driver.TestHelpers/MongoDB.Driver.TestHelpers.csproj @@ -13,7 +13,6 @@ - diff --git a/tests/MongoDB.Driver.Tests/MongoDB.Driver.Tests.csproj b/tests/MongoDB.Driver.Tests/MongoDB.Driver.Tests.csproj index e7a41e2e09b..316086d21ec 100644 --- a/tests/MongoDB.Driver.Tests/MongoDB.Driver.Tests.csproj +++ b/tests/MongoDB.Driver.Tests/MongoDB.Driver.Tests.csproj @@ -28,10 +28,6 @@ - - - - From fcbc397b63783357d5f10e63b9a29024451d4d23 Mon Sep 17 00:00:00 2001 From: rstam Date: Tue, 6 May 2025 12:00:39 -0700 Subject: [PATCH 05/24] CSHARP-3435: FilterDefinition Inject method should use root serializer --- .../InjectMethodToFilterTranslator.cs | 17 ++- .../Jira/CSharp3435Tests.cs | 112 ++++++++++++++++++ 2 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3435Tests.cs diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/MethodTranslators/InjectMethodToFilterTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/MethodTranslators/InjectMethodToFilterTranslator.cs index 61493819b50..a21ed9d567e 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/MethodTranslators/InjectMethodToFilterTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/MethodTranslators/InjectMethodToFilterTranslator.cs @@ -13,10 +13,12 @@ * limitations under the License. */ +using System.Linq; using System.Linq.Expressions; using System.Reflection; using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Filters; using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods; using MongoDB.Driver.Linq.Linq3Implementation.Misc; @@ -44,12 +46,21 @@ public static AstFilter Translate(TranslationContext context, MethodCallExpressi var filterExpression = arguments[0]; var filterDefinition = filterExpression.GetConstantValue(expression); var filterDefinitionType = filterDefinition.GetType(); // we KNOW it's a FilterDefinition because of the Inject method signature - var documentType = filterDefinitionType.GetGenericArguments()[0]; + var filterDefinitionDocumentType = filterDefinitionType.GetGenericArguments()[0]; + var rootSymbol = context.SymbolTable.Symbols.SingleOrDefault(s => s.Ast.IsRootVar()); + if (rootSymbol == null) + { + throw new ExpressionNotSupportedException(expression, because: "there is no current root symbol"); + } + var documentSerializer = rootSymbol.Serializer; + if (filterDefinitionDocumentType != documentSerializer.ValueType) + { + throw new ExpressionNotSupportedException(expression, because: $"FilterDefinition TDocument type: {filterDefinitionDocumentType} does not match document type {documentSerializer.ValueType} "); + } var serializerRegistry = BsonSerializer.SerializerRegistry; - var documentSerializer = serializerRegistry.GetSerializer(documentType); // TODO: is this the right serializer? - var renderFilterMethod = __renderFilterMethodInfo.MakeGenericMethod(documentType); + var renderFilterMethod = __renderFilterMethodInfo.MakeGenericMethod(filterDefinitionDocumentType); var renderedFilter = (BsonDocument)renderFilterMethod.Invoke(null, new[] { filterDefinition, documentSerializer, serializerRegistry, context.TranslationOptions }); return AstFilter.Raw(renderedFilter); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3435Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3435Tests.cs new file mode 100644 index 00000000000..33584f1b44e --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3435Tests.cs @@ -0,0 +1,112 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System.Linq; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira; + +public class CSharp3435Tests : LinqIntegrationTest +{ + public CSharp3435Tests(ClassFixture fixture) + : base(fixture) + { + } + + [Fact] + public void Where_should_work() + { + var queryable = CreateQueryable() + .Where(x => x.NormalizedUsername == "PAPLABROS"); + + var stages = Translate(Fixture.UserClaimCollection, queryable); + AssertStages( + stages, + "{ $project : { _outer : '$$ROOT', _id : 0 } }", + "{ $lookup : { from : 'Users', localField : '_outer.UserId', foreignField : '_id', as : '_inner' } }", + "{ $project : { claim : '$_outer', users : '$_inner', _id : 0 } }", + "{ $match : { 'claim.ClaimType' : 'Moderator' } }", + "{ $project : { _v : { $arrayElemAt : ['$users', 0] }, _id : 0 } }", + "{ $match : { '_v.NormalizedUsername' : 'PAPLABROS' } }"); + } + + [Fact] + public void Where_with_Inject_should_work() + { + var filter = Builders.Filter.Eq(x => x.NormalizedUsername, "PAPLABROS"); + var queryable = CreateQueryable() + .Where(x => filter.Inject()); + + var stages = Translate(Fixture.UserClaimCollection, queryable); + AssertStages( + stages, + "{ $project : { _outer : '$$ROOT', _id : 0 } }", + "{ $lookup : { from : 'Users', localField : '_outer.UserId', foreignField : '_id', as : '_inner' } }", + "{ $project : { claim : '$_outer', users : '$_inner', _id : 0 } }", + "{ $match : { 'claim.ClaimType' : 'Moderator' } }", + "{ $project : { _v : { $arrayElemAt : ['$users', 0] }, _id : 0 } }", + "{ $match : { '_v.NormalizedUsername' : 'PAPLABROS' } }"); + } + + public IQueryable CreateQueryable() + { + var usersCollection = Fixture.UserCollection; + var userClaimsCollection = Fixture.UserClaimCollection; + + var queryable = + from claim in userClaimsCollection.AsQueryable() + join user in usersCollection.AsQueryable() on claim.UserId equals user.Id into users + where claim.ClaimType == "Moderator" + select users.First(); + + // this is the equivalent method syntax + // var queryable = userClaimsCollection.AsQueryable() + // .GroupJoin( + // usersCollection.AsQueryable(), + // claim => claim.UserId, + // user => user.Id, + // (claim, users) => new { claim, users }) + // .Where(x => x.claim.ClaimType == "Moderator") + // .Select(x => x.users.First()); + + return queryable; + } + + public class User + { + public int Id { get; set; } + public string NormalizedUsername { get; set; } + } + + public class UserClaim + { + public int Id { get; set; } + public int UserId { get; set; } + public string ClaimType { get; set; } + } + + public sealed class ClassFixture : MongoDatabaseFixture + { + public IMongoCollection UserCollection { get; private set; } + public IMongoCollection UserClaimCollection { get; private set; } + + protected override void InitializeFixture() + { + UserCollection = CreateCollection("Users"); + UserClaimCollection = CreateCollection("UserClaims"); + } + } +} From ef4e942c0ba3c972632ce55ac1ab5fa753271842 Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Fri, 23 May 2025 09:42:27 -0700 Subject: [PATCH 06/24] CSHARP-5592: Separate unit tests into dedicated variants (#1695) --- build.cake | 2 +- evergreen/evergreen.yml | 86 ++++++++++++++++++- evergreen/install-dotnet.sh | 22 ++++- evergreen/run-unit-tests.sh | 11 +++ .../ConnectivityTests.cs | 1 + .../MongoDB.Bson.TestHelpers.csproj | 1 + .../Aws/AwsAuthenticationExamples.cs | 1 + .../CausalConsistencyExamples.cs | 1 + .../ChangeStreamExamples.cs | 1 + .../ClientEncryptionExamples.cs | 1 + .../ClientSideEncryption2Examples.cs | 1 + .../DocumentationExamples.cs | 31 +++---- .../ExplicitEncryptionExamples.cs | 1 + .../PrimerTestFixture.cs | 2 + .../StableApiExamples.cs | 1 + .../WithTransactionExample1.cs | 1 + .../IntegrationTest.cs | 2 +- .../MongoDB.Driver.TestHelpers.csproj | 1 + .../AggregateFluentFacetTests.cs | 2 +- ...tGraphLookupWithAirportsCollectionTests.cs | 1 + ...tGraphLookupWithEmployeeCollectionTests.cs | 1 + .../AggregateFluentTests.cs | 1 + ...egateGraphLookupEnumerableFromOrToTests.cs | 2 +- .../MongoDB.Driver.Tests/AsyncCursorTests.cs | 1 + .../AuthenticationTests.cs | 1 + .../CausalConsistencyTests.cs | 1 + .../ClientDocumentHelperTests.cs | 1 + tests/MongoDB.Driver.Tests/ClusterTests.cs | 1 + .../ConnectionsSurvivePrimaryStepDownTests.cs | 1 + .../Core/Bindings/CoreSessionTests.cs | 1 + .../Core/Connections/TcpStreamFactoryTests.cs | 1 + .../Core/Misc/WireVersionTests.cs | 1 + .../Core/Operations/OperationTestBase.cs | 1 + .../Core/Servers/ServerTests.cs | 1 + .../CustomServerSelectorTests.cs | 1 + .../Encryption/AutoEncryptionTests.cs | 1 + .../Encryption/ClientEncryptionTests.cs | 1 + .../FilterDefinitionBuilderTests.cs | 2 + tests/MongoDB.Driver.Tests/FindFluentTests.cs | 4 +- .../GridFS/GridFSBucketTests.cs | 3 + .../GridFS/GridFSDownloadStreamBaseTests.cs | 2 + .../GridFSSeekableDownloadStreamTests.cs | 1 + .../GridFS/GridFSUploadStreamTests.cs | 1 + .../Jira/CSharp2564Tests.cs | 1 + .../Jira/CSharp2622Tests.cs | 3 +- .../Jira/CSharp3188Tests.cs | 1 + .../Jira/CSharp3225Tests.cs | 1 + .../Jira/CSharp3397Tests.cs | 2 +- .../Jira/CSharp1486Tests.cs | 1 + .../Jira/CSharp1771Tests.cs | 1 + .../Jira/CSharp2071Tests.cs | 1 + .../Jira/CSharp2308Tests.cs | 1 + .../Jira/CSharp2422Tests.cs | 1 + .../Jira/CSharp2708Tests.cs | 2 +- .../Jira/CSharp2723Tests.cs | 2 +- .../Jira/CSharp3283Tests.cs | 1 + .../Jira/CSharp3425Tests.cs | 1 + .../Jira/CSharp3524Tests.cs | 1 + .../Jira/CSharp3630Tests.cs | 1 + .../Jira/CSharp3713Tests.cs | 1 + .../Jira/CSharp3865Tests.cs | 2 +- .../Jira/CSharp3910Tests.cs | 1 + .../Jira/CSharp3933Tests.cs | 2 +- .../Jira/CSharp3940Tests.cs | 1 + .../Linq3IntegrationTest.cs | 3 +- .../MongoQueryProviderTests.cs | 1 + .../IntegrationTestBase.cs | 2 + .../MongoQueryableEnumComparedToEnumTests.cs | 1 + ...aredToEnumWithStringRepresentationTests.cs | 1 + ...bleIntArrayComparedToEnumerableIntTests.cs | 1 + .../MongoQueryableIntComparedToDoubleTests.cs | 1 + ...edToDoubleWithStringRepresentationTests.cs | 1 + ...oQueryableIntComparedToNullableIntTests.cs | 1 + ...ullableIntWithStringRepresentationTests.cs | 1 + ...NullableEnumComparedToNullableEnumTests.cs | 1 + ...llableEnumWithStringRepresentationTests.cs | 1 + .../MongoQueryableWithDotNotationTests.cs | 1 + .../LegacyPredicateTranslatorTests.cs | 2 +- .../PredicateTranslatorValidationTests.cs | 2 +- .../ListDatabasesTests.cs | 1 + tests/MongoDB.Driver.Tests/LoggingTests.cs | 4 +- .../MongoClientSettingsTests.cs | 1 + .../MongoDB.Driver.Tests/MongoClientTests.cs | 1 + .../MongoCollectionImplTests.cs | 1 + .../MongoCredentialTests.cs | 1 + .../MongoDB.Driver.Tests/MongoDatabasTests.cs | 2 + .../MongoIndexManagerTests.cs | 1 + .../OfTypeMongoCollectionTests.cs | 1 + .../PinnedShardRouterTests.cs | 2 +- .../PipelineDefinitionBuilderTests.cs | 35 ++++---- .../PipelineStageDefinitionBuilderTests.cs | 37 ++++---- .../ReadPreferenceOnStandaloneTests.cs | 1 + .../RetryableWritesTests.cs | 1 + .../Samples/AggregationSample.cs | 1 + .../Search/MongoQueryableTests.cs | 1 + .../Search/VectorSearchTests.cs | 1 + .../Specifications/UnifiedTestSpecRunner.cs | 1 + .../prose-tests/ChangeStreamProseTests.cs | 1 + .../ClientSideEncryptionTestRunner.cs | 1 + .../prose-tests/ClientEncryptionProseTests.cs | 1 + ...onnectionMonitoringAndPoolingTestRunner.cs | 1 + .../Specifications/crud/CrudTestRunner.cs | 1 + .../crud/prose-tests/CrudProseTests.cs | 1 + .../InitialDnsSeedlistDiscoveryTestRunner.cs | 1 + .../RetryableReadsProseTests.cs | 1 + .../prose-tests/MMapV1Tests.cs | 1 + .../prose-tests/PoolClearRetryability.cs | 1 + .../prose-tests/RetryWriteOnOtherMongos.cs | 1 + .../ServerDiscoveryAndMonitoringProseTests.cs | 2 +- .../prose-tests/ServerDiscoveryProseTests.cs | 1 + .../sessions/SessionsProseTests.cs | 1 + .../uuid/prose-tests/ImplicitEncodingTests.cs | 1 + .../MongoDB.TestHelpers.csproj | 1 + .../IntegrationTestAttribute.cs | 25 ------ .../LibmongocryptTests.cs | 1 + .../LoggingTests.cs | 1 + 116 files changed, 286 insertions(+), 100 deletions(-) create mode 100644 evergreen/run-unit-tests.sh delete mode 100644 tests/MongoDB.TestHelpers/XunitExtensions/IntegrationTestAttribute.cs diff --git a/build.cake b/build.cake index 9c50bb96ffe..4c569d539c3 100644 --- a/build.cake +++ b/build.cake @@ -128,7 +128,7 @@ Task("Test") Console.WriteLine($"MONGO_X509_CLIENT_CERTIFICATE_PASSWORD={mongoX509ClientCertificatePassword}"); } - RunTests(buildConfig, testProject); + RunTests(buildConfig, testProject, filter: "Category=\"Integration\""); }) .DeferOnError(); diff --git a/evergreen/evergreen.yml b/evergreen/evergreen.yml index 8cbf5c9ba40..7437891078c 100644 --- a/evergreen/evergreen.yml +++ b/evergreen/evergreen.yml @@ -98,9 +98,13 @@ functions: install-dotnet: - command: shell.exec params: + include_expansions_in_env: + - "OS" + - "DOTNET_SDK_VERSION" + - "FRAMEWORK" script: | ${PREPARE_SHELL} - OS=${OS} DOTNET_SDK_VERSION=${DOTNET_SDK_VERSION} bash ${PROJECT_DIRECTORY}/evergreen/install-dotnet.sh + bash ${PROJECT_DIRECTORY}/evergreen/install-dotnet.sh prepare-resources: - command: shell.exec @@ -352,6 +356,18 @@ functions: cd ${DRIVERS_TOOLS}/.evergreen DRIVERS_TOOLS=${DRIVERS_TOOLS} MONGODB_URI=${MONGODB_URI} bash ${DRIVERS_TOOLS}/.evergreen/run-load-balancer.sh stop + run-unit-tests: + - command: shell.exec + type: test + params: + working_dir: mongo-csharp-driver + shell: "bash" + include_expansions_in_env: + - "FRAMEWORK" + script: | + ${PREPARE_SHELL} + bash evergreen/run-unit-tests.sh + run-tests: - command: shell.exec type: test @@ -508,7 +524,7 @@ functions: echo "Response Body: $response_body" echo "HTTP Status: $http_status" - + assume-ec2-role: - command: ec2.assume_role params: @@ -1031,6 +1047,36 @@ post: - func: cleanup tasks: + - name: unit-tests-net472 + commands: + - command: expansions.update + params: + updates: + - key: 'FRAMEWORK' + value: 'net472' + - func: install-dotnet + - func: run-unit-tests + + - name: unit-tests-netstandard21 + commands: + - command: expansions.update + params: + updates: + - key: 'FRAMEWORK' + value: 'netstandard2.1' + - func: install-dotnet + - func: run-unit-tests + + - name: unit-tests-net60 + commands: + - command: expansions.update + params: + updates: + - key: 'FRAMEWORK' + value: 'net6.0' + - func: install-dotnet + - func: run-unit-tests + - name: test-net472 commands: - func: setup-csfle-secrets @@ -2179,6 +2225,42 @@ task_groups: - validate-apicompat buildvariants: +- name: unit-tests-windows + display_name: Unit Tests on Windows + run_on: windows-64-vs2017-test + expansions: + OS: "windows-64" + tasks: + - name: unit-tests-net472 + - name: unit-tests-netstandard21 + - name: unit-tests-net60 + +- name: unit-tests-ubuntu + display_name: Unit Tests on Ubuntu + run_on: ubuntu2004-small + expansions: + OS: "ubuntu-2004" + tasks: + - name: unit-tests-netstandard21 + - name: unit-tests-net60 + +- name: unit-tests-macos + display_name: Unit Tests on MacOs + run_on: macos-14 + expansions: + OS: "macos-14" + tasks: + - name: unit-tests-netstandard21 + - name: unit-tests-net60 + +- name: unit-tests-macos-arm + display_name: Unit Tests on MacOs Arm + run_on: macos-14-arm64 + expansions: + OS: "macos-14-arm64" + tasks: + - name: unit-tests-net60 + - matrix_name: stable-api-tests matrix_spec: { version: ["5.0", "6.0", "7.0", "8.0", "rapid", "latest"], topology: "standalone", auth: "auth", ssl: "nossl", os: "windows-64" } display_name: "Stable API ${version} ${topology} ${auth} ${ssl} ${os}" diff --git a/evergreen/install-dotnet.sh b/evergreen/install-dotnet.sh index 230eb2ab5bd..0112e960200 100644 --- a/evergreen/install-dotnet.sh +++ b/evergreen/install-dotnet.sh @@ -4,14 +4,32 @@ set -o errexit # Exit the script with error if any of the commands fail DOTNET_SDK_PATH="${DOTNET_SDK_PATH:-./.dotnet}" DOTNET_SDK_VERSION="${DOTNET_SDK_VERSION:-8.0}" +echo "runtime: $FRAMEWORK" + +if [ -n "$FRAMEWORK" ]; then + if [ "$FRAMEWORK" = "net6.0" ]; then + RUNTIME_VERSION="6.0" + elif [ "$FRAMEWORK" = "netstandard2.1" ]; then + RUNTIME_VERSION="3.1" + fi +fi + if [[ $OS =~ [Ww]indows.* ]]; then echo "Downloading Windows .NET SDK installer into $DOTNET_SDK_PATH folder..." curl -Lfo ./dotnet-install.ps1 https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.ps1 - echo "Installing .NET 8.0 SDK..." + echo "Installing .NET ${DOTNET_SDK_VERSION} SDK..." powershell.exe ./dotnet-install.ps1 -Channel "$DOTNET_SDK_VERSION" -InstallDir "$DOTNET_SDK_PATH" -NoPath + if [ -n "$RUNTIME_VERSION" ]; then + echo "Installing .NET ${RUNTIME_VERSION} runtime..." + powershell.exe ./dotnet-install.ps1 -Channel "$RUNTIME_VERSION" -Runtime dotnet -InstallDir "$DOTNET_SDK_PATH" -NoPath + fi else echo "Downloading .NET SDK installer into $DOTNET_SDK_PATH folder..." curl -Lfo ./dotnet-install.sh https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.sh - echo "Installing .NET 8.0 SDK..." + echo "Installing .NET ${DOTNET_SDK_VERSION} SDK..." bash ./dotnet-install.sh --channel "$DOTNET_SDK_VERSION" --install-dir "$DOTNET_SDK_PATH" --no-path + if [ -n "$RUNTIME_VERSION" ]; then + echo "Installing .NET ${RUNTIME_VERSION} runtime..." + bash ./dotnet-install.sh --channel "$RUNTIME_VERSION" --runtime dotnet --install-dir "$DOTNET_SDK_PATH" --no-path + fi fi diff --git a/evergreen/run-unit-tests.sh b/evergreen/run-unit-tests.sh new file mode 100644 index 00000000000..397d5bb6701 --- /dev/null +++ b/evergreen/run-unit-tests.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -o errexit # Exit the script with error if any of the commands fail + +FRAMEWORK=${FRAMEWORK:-net6.0} + +if [ "$FRAMEWORK" = "netstandard2.1" ]; then + FRAMEWORK="netcoreapp3.1" +fi + +dotnet build +dotnet test --no-build --filter "Category!=Integration" -f "$FRAMEWORK" --results-directory ./build/test-results --logger "junit;verbosity=detailed;LogFileName=TEST-{assembly}.xml;FailureBodyFormat=Verbose" --logger "console;verbosity=detailed" diff --git a/tests/AtlasConnectivity.Tests/ConnectivityTests.cs b/tests/AtlasConnectivity.Tests/ConnectivityTests.cs index b5729dd8d4c..96cb70134dc 100644 --- a/tests/AtlasConnectivity.Tests/ConnectivityTests.cs +++ b/tests/AtlasConnectivity.Tests/ConnectivityTests.cs @@ -23,6 +23,7 @@ namespace AtlasConnectivity.Tests { + [Trait("Category", "Integration")] public class ConnectivityTests : LoggableTestClass { // public constructors diff --git a/tests/MongoDB.Bson.TestHelpers/MongoDB.Bson.TestHelpers.csproj b/tests/MongoDB.Bson.TestHelpers/MongoDB.Bson.TestHelpers.csproj index 00789c4be93..e37dbff7c1b 100644 --- a/tests/MongoDB.Bson.TestHelpers/MongoDB.Bson.TestHelpers.csproj +++ b/tests/MongoDB.Bson.TestHelpers/MongoDB.Bson.TestHelpers.csproj @@ -2,6 +2,7 @@ + false ..\..\MongoDBLegacyTest.ruleset diff --git a/tests/MongoDB.Driver.Examples/Aws/AwsAuthenticationExamples.cs b/tests/MongoDB.Driver.Examples/Aws/AwsAuthenticationExamples.cs index 8537d73e500..bba5b707e9d 100644 --- a/tests/MongoDB.Driver.Examples/Aws/AwsAuthenticationExamples.cs +++ b/tests/MongoDB.Driver.Examples/Aws/AwsAuthenticationExamples.cs @@ -53,6 +53,7 @@ namespace MongoDB.Driver.Examples.Aws /// 4. To work with EC2 container credentials from EC2 instance metadata make sure a test is launched on EC2 env and AWS_CONTAINER_CREDENTIALS_* is not set /// 5. To work with Aws WebIdentityToken make sure that AWS_WEB_IDENTITY_TOKEN_FILE, AWS_ROLE_ARN and AWS_ROLE_SESSION_NAME are configured /// + [Trait("Category", "Integration")] public class AwsAuthenticationExamples { private static readonly string __connectionStringHosts = ""; diff --git a/tests/MongoDB.Driver.Examples/CausalConsistencyExamples.cs b/tests/MongoDB.Driver.Examples/CausalConsistencyExamples.cs index da6a023a678..a2c1e5a069b 100644 --- a/tests/MongoDB.Driver.Examples/CausalConsistencyExamples.cs +++ b/tests/MongoDB.Driver.Examples/CausalConsistencyExamples.cs @@ -22,6 +22,7 @@ namespace MongoDB.Driver.Examples { + [Trait("Category", "Integration")] public class CausalConsistencyExamples { [Fact] diff --git a/tests/MongoDB.Driver.Examples/ChangeStreamExamples.cs b/tests/MongoDB.Driver.Examples/ChangeStreamExamples.cs index d3731364c1f..730dfe80d1f 100644 --- a/tests/MongoDB.Driver.Examples/ChangeStreamExamples.cs +++ b/tests/MongoDB.Driver.Examples/ChangeStreamExamples.cs @@ -25,6 +25,7 @@ namespace MongoDB.Driver.Examples { + [Trait("Category", "Integration")] public class ChangeStreamExamples { [Fact] diff --git a/tests/MongoDB.Driver.Examples/ClientEncryptionExamples.cs b/tests/MongoDB.Driver.Examples/ClientEncryptionExamples.cs index ae7560e42a6..a2c7a54051f 100644 --- a/tests/MongoDB.Driver.Examples/ClientEncryptionExamples.cs +++ b/tests/MongoDB.Driver.Examples/ClientEncryptionExamples.cs @@ -25,6 +25,7 @@ namespace MongoDB.Driver.Examples { + [Trait("Category", "Integration")] public class ClientEncryptionExamples { private const string LocalMasterKey = "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk"; diff --git a/tests/MongoDB.Driver.Examples/ClientSideEncryption2Examples.cs b/tests/MongoDB.Driver.Examples/ClientSideEncryption2Examples.cs index 0782fad3fd1..2524e5cb378 100644 --- a/tests/MongoDB.Driver.Examples/ClientSideEncryption2Examples.cs +++ b/tests/MongoDB.Driver.Examples/ClientSideEncryption2Examples.cs @@ -27,6 +27,7 @@ namespace MongoDB.Driver.Examples { + [Trait("Category", "Integration")] public class ClientSideEncryption2Examples { private const string LocalMasterKey = "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk"; diff --git a/tests/MongoDB.Driver.Examples/DocumentationExamples.cs b/tests/MongoDB.Driver.Examples/DocumentationExamples.cs index 4ce1ca6f8a8..5903574dcd0 100644 --- a/tests/MongoDB.Driver.Examples/DocumentationExamples.cs +++ b/tests/MongoDB.Driver.Examples/DocumentationExamples.cs @@ -23,6 +23,7 @@ namespace MongoDB.Driver.Examples { + [Trait("Category", "Integration")] public class DocumentationExamples { private readonly IMongoClient client; @@ -41,7 +42,7 @@ public DocumentationExamples() [Fact] public void Example_1() { - // db.inventory.insertOne( { item: "canvas", qty: 100, tags: ["cotton"], size: { h: 28, w: 35.5, uom: "cm" } } ) + // db.inventory.insertOne( { item: "canvas", qty: 100, tags: ["cotton"], size: { h: 28, w: 35.5, uom: "cm" } } ) // Start Example 1 var document = new BsonDocument @@ -76,10 +77,10 @@ public void Example_2() [Fact] public void Example_3() { - // db.inventory.insertMany([ + // db.inventory.insertMany([ // { item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } }, // { item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } }, - // { item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } } ]) + // { item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } } ]) // Start Example 3 var documents = new BsonDocument[] @@ -125,7 +126,7 @@ public void Example_6() // { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" }, // { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" }, // { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" }, - // { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" } ]) + // { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" } ]) // Start Example 6 var documents = new BsonDocument[] @@ -265,7 +266,7 @@ public void Example_13() [Fact] public void Example_14() { - // db.inventory.insertMany( [ + // db.inventory.insertMany( [ // { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" }, // { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" }, // { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" }, @@ -392,7 +393,7 @@ public void Example_19() [Fact] public void Example_20() { - // db.inventory.insertMany([ + // db.inventory.insertMany([ // { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] }, // { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] }, // { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] }, @@ -559,7 +560,7 @@ public void Example_28() [Fact] public void Example_29() { - // db.inventory.insertMany( [ + // db.inventory.insertMany( [ // { item: "journal", instock: [ { warehouse: "A", qty: 5 }, { warehouse: "C", qty: 15 } ] }, // { item: "notebook", instock: [ { warehouse: "C", qty: 5 } ] }, // { item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 15 } ] }, @@ -795,7 +796,7 @@ public void Example_41() [Fact] public void Example_42() { - // db.inventory.insertMany( [ + // db.inventory.insertMany( [ // { item: "journal", status: "A", size: { h: 14, w: 21, uom: "cm" }, instock: [ { warehouse: "A", qty: 5 } ] }, // { item: "notebook", status: "A", size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "C", qty: 5 } ] }, // { item: "paper", status: "D", size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "A", qty: 60 } ] }, @@ -992,7 +993,7 @@ public void Example_50() [Fact] public void Example_51() { - // db.inventory.insertMany( [ + // db.inventory.insertMany( [ // { item: "canvas", qty: 100, size: { h: 28, w: 35.5, uom: "cm" }, status: "A" }, // { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" }, // { item: "mat", qty: 85, size: { h: 27.9, w: 35.5, uom: "cm" }, status: "A" }, @@ -1002,7 +1003,7 @@ public void Example_51() // { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" }, // { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }, // { item: "sketchbook", qty: 80, size: { h: 14, w: 21, uom: "cm" }, status: "A" }, - // { item: "sketch pad", qty: 95, size: { h: 22.85, w: 30.5, uom: "cm" }, status: "A" } ]); + // { item: "sketch pad", qty: 95, size: { h: 22.85, w: 30.5, uom: "cm" }, status: "A" } ]); // Start Example 51 var documents = new[] @@ -1149,12 +1150,12 @@ public void Example_54() [Fact] public void Example_55() { - // db.inventory.insertMany( [ + // db.inventory.insertMany( [ // { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" }, // { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "P" }, // { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" }, // { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" }, - // { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }, ]); + // { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }, ]); // Start Example 55 var documents = new[] @@ -1252,7 +1253,7 @@ public void Aggregation_Example_1() { RequireServer.Check(); - //db.sales.aggregate([ + //db.sales.aggregate([ // { $match : { "items.fruit":"banana" } }, // { $sort : { "date" : 1 } } //]) @@ -1347,7 +1348,7 @@ public void Aggregation_Example_3() // } //}, //{ - // $project: { day: "$_id.day", revenue: 1, items_sold: 1, + // $project: { day: "$_id.day", revenue: 1, items_sold: 1, // discount: { $cond: { if : { $lte: ["$revenue", 250] }, then: 25, else : 0 }} // } //}]) @@ -1421,7 +1422,7 @@ public void Aggregation_Example_4() // $project : { // "_id" : 0, // "name" : 1, - // airlines : { + // airlines : { // $filter : { // input : "$airlines", // as : "airline", diff --git a/tests/MongoDB.Driver.Examples/ExplicitEncryptionExamples.cs b/tests/MongoDB.Driver.Examples/ExplicitEncryptionExamples.cs index ae948248240..3676919afae 100644 --- a/tests/MongoDB.Driver.Examples/ExplicitEncryptionExamples.cs +++ b/tests/MongoDB.Driver.Examples/ExplicitEncryptionExamples.cs @@ -25,6 +25,7 @@ namespace MongoDB.Driver.Examples { + [Trait("Category", "Integration")] public class ExplicitEncryptionExamples { private const string LocalMasterKey = "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk"; diff --git a/tests/MongoDB.Driver.Examples/PrimerTestFixture.cs b/tests/MongoDB.Driver.Examples/PrimerTestFixture.cs index e6982eafa17..65288622f30 100644 --- a/tests/MongoDB.Driver.Examples/PrimerTestFixture.cs +++ b/tests/MongoDB.Driver.Examples/PrimerTestFixture.cs @@ -18,9 +18,11 @@ using System.IO; using System.Reflection; using MongoDB.Bson; +using Xunit; namespace MongoDB.Driver.Examples { + [Trait("Category", "Integration")] public abstract class PrimerTestFixture { protected static IMongoClient __client; diff --git a/tests/MongoDB.Driver.Examples/StableApiExamples.cs b/tests/MongoDB.Driver.Examples/StableApiExamples.cs index 56bcb2900e8..8d636aa63e0 100644 --- a/tests/MongoDB.Driver.Examples/StableApiExamples.cs +++ b/tests/MongoDB.Driver.Examples/StableApiExamples.cs @@ -19,6 +19,7 @@ namespace MongoDB.Driver.Examples { + [Trait("Category", "Integration")] public class StableApiExamples { [Fact] diff --git a/tests/MongoDB.Driver.Examples/TransactionExamplesForDocs/WithTransactionExample1.cs b/tests/MongoDB.Driver.Examples/TransactionExamplesForDocs/WithTransactionExample1.cs index 9251a5da4e7..66da76dc1a0 100644 --- a/tests/MongoDB.Driver.Examples/TransactionExamplesForDocs/WithTransactionExample1.cs +++ b/tests/MongoDB.Driver.Examples/TransactionExamplesForDocs/WithTransactionExample1.cs @@ -23,6 +23,7 @@ namespace MongoDB.Driver.Examples.TransactionExamplesForDocs { + [Trait("Category", "Integration")] public class WithTransactionExample1 { [Fact] diff --git a/tests/MongoDB.Driver.TestHelpers/IntegrationTest.cs b/tests/MongoDB.Driver.TestHelpers/IntegrationTest.cs index c0424339d5f..f373a4d897e 100644 --- a/tests/MongoDB.Driver.TestHelpers/IntegrationTest.cs +++ b/tests/MongoDB.Driver.TestHelpers/IntegrationTest.cs @@ -20,7 +20,7 @@ namespace MongoDB.Driver.Tests { - [IntegrationTest] + [Trait("Category", "Integration")] public abstract class IntegrationTest : IClassFixture where TFixture : MongoDatabaseFixture { diff --git a/tests/MongoDB.Driver.TestHelpers/MongoDB.Driver.TestHelpers.csproj b/tests/MongoDB.Driver.TestHelpers/MongoDB.Driver.TestHelpers.csproj index 096fcb8bc0b..e1faee5876f 100644 --- a/tests/MongoDB.Driver.TestHelpers/MongoDB.Driver.TestHelpers.csproj +++ b/tests/MongoDB.Driver.TestHelpers/MongoDB.Driver.TestHelpers.csproj @@ -2,6 +2,7 @@ + false ..\..\MongoDBTest.ruleset diff --git a/tests/MongoDB.Driver.Tests/AggregateFluentFacetTests.cs b/tests/MongoDB.Driver.Tests/AggregateFluentFacetTests.cs index c248cdc75cc..71c13c2577b 100644 --- a/tests/MongoDB.Driver.Tests/AggregateFluentFacetTests.cs +++ b/tests/MongoDB.Driver.Tests/AggregateFluentFacetTests.cs @@ -23,11 +23,11 @@ using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; -using MongoDB.Driver.Linq; using Xunit; namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class AggregateFluentFacetTests { #region static diff --git a/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithAirportsCollectionTests.cs b/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithAirportsCollectionTests.cs index 29142383c79..3bccb211750 100644 --- a/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithAirportsCollectionTests.cs +++ b/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithAirportsCollectionTests.cs @@ -26,6 +26,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class AggregateFluentGraphLookupWithAirportCollectionTests { #region static diff --git a/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithEmployeeCollectionTests.cs b/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithEmployeeCollectionTests.cs index f2df2ed9e72..291b675ccc2 100644 --- a/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithEmployeeCollectionTests.cs +++ b/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithEmployeeCollectionTests.cs @@ -27,6 +27,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class AggregateFluentGraphLookupWithEmployeeCollectionTests { #region static diff --git a/tests/MongoDB.Driver.Tests/AggregateFluentTests.cs b/tests/MongoDB.Driver.Tests/AggregateFluentTests.cs index ea4c6c9a3b2..405ec767829 100644 --- a/tests/MongoDB.Driver.Tests/AggregateFluentTests.cs +++ b/tests/MongoDB.Driver.Tests/AggregateFluentTests.cs @@ -31,6 +31,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class AggregateFluentTests { [Theory] diff --git a/tests/MongoDB.Driver.Tests/AggregateGraphLookupEnumerableFromOrToTests.cs b/tests/MongoDB.Driver.Tests/AggregateGraphLookupEnumerableFromOrToTests.cs index fee02d81a62..87c3af7d176 100644 --- a/tests/MongoDB.Driver.Tests/AggregateGraphLookupEnumerableFromOrToTests.cs +++ b/tests/MongoDB.Driver.Tests/AggregateGraphLookupEnumerableFromOrToTests.cs @@ -16,12 +16,12 @@ using System.Collections.Generic; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class AggregateGraphLookupEnumerableFromOrToTests { // public methods diff --git a/tests/MongoDB.Driver.Tests/AsyncCursorTests.cs b/tests/MongoDB.Driver.Tests/AsyncCursorTests.cs index e9864e8d1c8..72fe1a39f88 100644 --- a/tests/MongoDB.Driver.Tests/AsyncCursorTests.cs +++ b/tests/MongoDB.Driver.Tests/AsyncCursorTests.cs @@ -30,6 +30,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class AsyncCursorTests { //public methods diff --git a/tests/MongoDB.Driver.Tests/AuthenticationTests.cs b/tests/MongoDB.Driver.Tests/AuthenticationTests.cs index 4e84d4ef060..f60b30c80c5 100644 --- a/tests/MongoDB.Driver.Tests/AuthenticationTests.cs +++ b/tests/MongoDB.Driver.Tests/AuthenticationTests.cs @@ -30,6 +30,7 @@ namespace MongoDB.Driver.Tests /// /// Authentication integration tests. /// + [Trait("Category", "Integration")] public class AuthenticationTests { [Theory] diff --git a/tests/MongoDB.Driver.Tests/CausalConsistencyTests.cs b/tests/MongoDB.Driver.Tests/CausalConsistencyTests.cs index 32d3afdbcf5..c715b00f8e9 100644 --- a/tests/MongoDB.Driver.Tests/CausalConsistencyTests.cs +++ b/tests/MongoDB.Driver.Tests/CausalConsistencyTests.cs @@ -28,6 +28,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class CausalConsistencyTests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/ClientDocumentHelperTests.cs b/tests/MongoDB.Driver.Tests/ClientDocumentHelperTests.cs index 5f714d07ba2..7eaa434a223 100644 --- a/tests/MongoDB.Driver.Tests/ClientDocumentHelperTests.cs +++ b/tests/MongoDB.Driver.Tests/ClientDocumentHelperTests.cs @@ -31,6 +31,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class ClientDocumentHelperTests { private static readonly string __longAString = new string('a', 512); diff --git a/tests/MongoDB.Driver.Tests/ClusterTests.cs b/tests/MongoDB.Driver.Tests/ClusterTests.cs index 97732fc09ee..1852264edc3 100644 --- a/tests/MongoDB.Driver.Tests/ClusterTests.cs +++ b/tests/MongoDB.Driver.Tests/ClusterTests.cs @@ -37,6 +37,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class ClusterTests : LoggableTestClass { private static readonly HashSet __commandsToNotCapture = new HashSet diff --git a/tests/MongoDB.Driver.Tests/ConnectionsSurvivePrimaryStepDownTests.cs b/tests/MongoDB.Driver.Tests/ConnectionsSurvivePrimaryStepDownTests.cs index 5c30e8c1895..f37cd6c4b11 100644 --- a/tests/MongoDB.Driver.Tests/ConnectionsSurvivePrimaryStepDownTests.cs +++ b/tests/MongoDB.Driver.Tests/ConnectionsSurvivePrimaryStepDownTests.cs @@ -31,6 +31,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class ConnectionsSurvivePrimaryStepDownTests { private readonly string _collectionName = "step-down"; diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/CoreSessionTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/CoreSessionTests.cs index 6b3496813e1..4f07e47b740 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/CoreSessionTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/CoreSessionTests.cs @@ -224,6 +224,7 @@ public void Dispose_should_have_expected_result( } [Fact] + [Trait("Category", "Integration")] public void StartTransaction_should_throw_when_write_concern_is_unacknowledged() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet).Supports(Feature.Transactions); diff --git a/tests/MongoDB.Driver.Tests/Core/Connections/TcpStreamFactoryTests.cs b/tests/MongoDB.Driver.Tests/Core/Connections/TcpStreamFactoryTests.cs index 014f1b6a523..cfb8f8194c5 100644 --- a/tests/MongoDB.Driver.Tests/Core/Connections/TcpStreamFactoryTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Connections/TcpStreamFactoryTests.cs @@ -29,6 +29,7 @@ namespace MongoDB.Driver.Core.Connections { + [Trait("Category", "Integration")] public class TcpStreamFactoryTests { [Theory] diff --git a/tests/MongoDB.Driver.Tests/Core/Misc/WireVersionTests.cs b/tests/MongoDB.Driver.Tests/Core/Misc/WireVersionTests.cs index 7c94537bc7b..161b4908c0e 100644 --- a/tests/MongoDB.Driver.Tests/Core/Misc/WireVersionTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Misc/WireVersionTests.cs @@ -24,6 +24,7 @@ namespace MongoDB.Driver.Core.Tests.Core.Misc public class WireVersionTests { [Fact] + [Trait("Category", "Integration")] public void Server_maxWireVersion_should_be_in_supported_range() { RequireServer.Check().StableServer(stable: true); diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/OperationTestBase.cs b/tests/MongoDB.Driver.Tests/Core/Operations/OperationTestBase.cs index b323f4e7e1a..c90c70c6132 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/OperationTestBase.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/OperationTestBase.cs @@ -30,6 +30,7 @@ namespace MongoDB.Driver.Core.Operations { + [Trait("Category", "Integration")] public abstract class OperationTestBase : IDisposable { private protected IClusterInternal _cluster; diff --git a/tests/MongoDB.Driver.Tests/Core/Servers/ServerTests.cs b/tests/MongoDB.Driver.Tests/Core/Servers/ServerTests.cs index 6ecc3ac6234..d0c56e46227 100644 --- a/tests/MongoDB.Driver.Tests/Core/Servers/ServerTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Servers/ServerTests.cs @@ -883,6 +883,7 @@ private Server SetupServer(bool exceptionOnConnectionOpen, bool exceptionOnConne } } + [Trait("Category", "Integration")] public class ServerChannelTests { [Theory] diff --git a/tests/MongoDB.Driver.Tests/CustomServerSelectorTests.cs b/tests/MongoDB.Driver.Tests/CustomServerSelectorTests.cs index def4ac29b96..14eb15bc38c 100644 --- a/tests/MongoDB.Driver.Tests/CustomServerSelectorTests.cs +++ b/tests/MongoDB.Driver.Tests/CustomServerSelectorTests.cs @@ -27,6 +27,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class CustomServerSelectorTests : LoggableTestClass { public CustomServerSelectorTests(ITestOutputHelper output) : base(output) diff --git a/tests/MongoDB.Driver.Tests/Encryption/AutoEncryptionTests.cs b/tests/MongoDB.Driver.Tests/Encryption/AutoEncryptionTests.cs index 546897cbdbc..6caa5998af3 100644 --- a/tests/MongoDB.Driver.Tests/Encryption/AutoEncryptionTests.cs +++ b/tests/MongoDB.Driver.Tests/Encryption/AutoEncryptionTests.cs @@ -32,6 +32,7 @@ namespace MongoDB.Driver.Tests.Encryption { [Trait("Category", "CSFLE")] + [Trait("Category", "Integration")] public class AutoEncryptionTests : LoggableTestClass { #region static diff --git a/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs b/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs index 3cb767016ea..69cb78ed104 100644 --- a/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs +++ b/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs @@ -37,6 +37,7 @@ namespace MongoDB.Driver.Tests.Encryption { [Trait("Category", "CSFLE")] + [Trait("Category", "Integration")] public class ClientEncryptionTests { #region static diff --git a/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderTests.cs b/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderTests.cs index d51e76fd149..1cd36373e43 100644 --- a/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderTests.cs +++ b/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderTests.cs @@ -1140,6 +1140,7 @@ private enum ProductType } } + [Trait("Category", "Integration")] public class FieldDefinitionBuilderUInt32Tests { #region static @@ -1361,6 +1362,7 @@ private class DocumentWithUInt32Field } } + [Trait("Category", "Integration")] public class FieldDefinitionBuilderUInt64Tests { #region static diff --git a/tests/MongoDB.Driver.Tests/FindFluentTests.cs b/tests/MongoDB.Driver.Tests/FindFluentTests.cs index fb70cd6f43c..78faff1ec9b 100644 --- a/tests/MongoDB.Driver.Tests/FindFluentTests.cs +++ b/tests/MongoDB.Driver.Tests/FindFluentTests.cs @@ -303,8 +303,6 @@ public void ToCursor_should_call_collection_Find_with_expected_arguments( [Fact] public void ToString_should_return_the_correct_string() { - RequireServer.Check().Supports(Feature.FindProjectionExpressions); - var subject = CreateSubject(); subject.Filter = new BsonDocument("Age", 20); subject.Options.Collation = new Collation("en_US"); @@ -325,7 +323,7 @@ public void ToString_should_return_the_correct_string() var str = find.ToString(); - var expectedProjection = + var expectedProjection = "{ \"_v\" : { \"$concat\" : [\"$FirstName\", \" \", \"$LastName\"] }, \"_id\" : 0 }"; str.Should().Be( diff --git a/tests/MongoDB.Driver.Tests/GridFS/GridFSBucketTests.cs b/tests/MongoDB.Driver.Tests/GridFS/GridFSBucketTests.cs index 3ead29d9c82..f4e969d1085 100644 --- a/tests/MongoDB.Driver.Tests/GridFS/GridFSBucketTests.cs +++ b/tests/MongoDB.Driver.Tests/GridFS/GridFSBucketTests.cs @@ -327,6 +327,7 @@ public void DownloadToStreamByName_should_throw_when_filename_is_null( [Theory] [ParameterAttributeData] + [Trait("Category", "Integration")] public void Drop_should_drop_the_files_and_chunks_collections( [Values(false, true)] bool async) { @@ -352,6 +353,7 @@ public void Drop_should_drop_the_files_and_chunks_collections( [Theory] [ParameterAttributeData] + [Trait("Category", "Integration")] public void Drop_should_throw_when_a_write_concern_error_occurss( [Values(false, true)] bool async) @@ -629,6 +631,7 @@ public void UploadFromStream_should_throw_when_source_is_null( [Theory] [ParameterAttributeData] + [Trait("Category", "Integration")] public void GridFS_should_work_with_strict_stable_api( [Values(false, true)] bool async) { diff --git a/tests/MongoDB.Driver.Tests/GridFS/GridFSDownloadStreamBaseTests.cs b/tests/MongoDB.Driver.Tests/GridFS/GridFSDownloadStreamBaseTests.cs index 210cf0c8e4c..948abb45c1c 100644 --- a/tests/MongoDB.Driver.Tests/GridFS/GridFSDownloadStreamBaseTests.cs +++ b/tests/MongoDB.Driver.Tests/GridFS/GridFSDownloadStreamBaseTests.cs @@ -146,6 +146,7 @@ public void constructor_should_initialize_instance() [Theory] [ParameterAttributeData] + [Trait("Category", "Integration")] public void CopyTo_should_copy_stream( [Values(0.0, 0.5, 1.0, 1.5, 2.0, 2.5)] double contentSizeMultiple, [Values(null, 128)] int? bufferSize, @@ -219,6 +220,7 @@ public void FileInfo_should_return_expected_result() [Theory] [ParameterAttributeData] + [Trait("Category", "Integration")] public void Flush_should_not_throw( [Values(false, true)] bool async) { diff --git a/tests/MongoDB.Driver.Tests/GridFS/GridFSSeekableDownloadStreamTests.cs b/tests/MongoDB.Driver.Tests/GridFS/GridFSSeekableDownloadStreamTests.cs index a6f336c4f6c..f291adf375e 100644 --- a/tests/MongoDB.Driver.Tests/GridFS/GridFSSeekableDownloadStreamTests.cs +++ b/tests/MongoDB.Driver.Tests/GridFS/GridFSSeekableDownloadStreamTests.cs @@ -94,6 +94,7 @@ public void RetryReads_get_and_set_should_work( [Theory] [ParameterAttributeData] + [Trait("Category", "Integration")] public void Read_should_return_expected_result( [Values(0.0, 0.5, 1.0, 1.5, 2.0, 2.5)] double fileLengthMultiple, [Values(0.0, 0.5)] double positionMultiple, diff --git a/tests/MongoDB.Driver.Tests/GridFS/GridFSUploadStreamTests.cs b/tests/MongoDB.Driver.Tests/GridFS/GridFSUploadStreamTests.cs index 15070c8b0f6..2be46bb999c 100644 --- a/tests/MongoDB.Driver.Tests/GridFS/GridFSUploadStreamTests.cs +++ b/tests/MongoDB.Driver.Tests/GridFS/GridFSUploadStreamTests.cs @@ -27,6 +27,7 @@ namespace MongoDB.Driver.Tests.GridFS { + [Trait("Category", "Integration")] public class GridFSUploadStreamTests { // public methods diff --git a/tests/MongoDB.Driver.Tests/Jira/CSharp2564Tests.cs b/tests/MongoDB.Driver.Tests/Jira/CSharp2564Tests.cs index 86506f44c21..0bfd0b6aefc 100644 --- a/tests/MongoDB.Driver.Tests/Jira/CSharp2564Tests.cs +++ b/tests/MongoDB.Driver.Tests/Jira/CSharp2564Tests.cs @@ -28,6 +28,7 @@ namespace MongoDB.Driver.Tests.Jira { + [Trait("Category", "Integration")] public class CSharp2564Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Jira/CSharp2622Tests.cs b/tests/MongoDB.Driver.Tests/Jira/CSharp2622Tests.cs index a87e83779dd..8e759cbe30b 100644 --- a/tests/MongoDB.Driver.Tests/Jira/CSharp2622Tests.cs +++ b/tests/MongoDB.Driver.Tests/Jira/CSharp2622Tests.cs @@ -13,15 +13,14 @@ * limitations under the License. */ -using System; using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; -using MongoDB.Bson.Serialization.Options; using Xunit; namespace MongoDB.Driver.Tests.Jira { + [Trait("Category", "Integration")] public class CSharp2622Tests { // public methods diff --git a/tests/MongoDB.Driver.Tests/Jira/CSharp3188Tests.cs b/tests/MongoDB.Driver.Tests/Jira/CSharp3188Tests.cs index c3395ad3d04..019ca0c425e 100644 --- a/tests/MongoDB.Driver.Tests/Jira/CSharp3188Tests.cs +++ b/tests/MongoDB.Driver.Tests/Jira/CSharp3188Tests.cs @@ -26,6 +26,7 @@ namespace MongoDB.Driver.Tests.Jira { + [Trait("Category", "Integration")] public class CSharp3188Tests { [Theory] diff --git a/tests/MongoDB.Driver.Tests/Jira/CSharp3225Tests.cs b/tests/MongoDB.Driver.Tests/Jira/CSharp3225Tests.cs index 7241343353b..fcf66974e75 100644 --- a/tests/MongoDB.Driver.Tests/Jira/CSharp3225Tests.cs +++ b/tests/MongoDB.Driver.Tests/Jira/CSharp3225Tests.cs @@ -24,6 +24,7 @@ namespace MongoDB.Driver.Tests.Jira { + [Trait("Category", "Integration")] public class CSharp3225Tests { // these examples are taken from: https://www.mongodb.com/docs/manual/reference/operator/aggregation/setWindowFields/#examples diff --git a/tests/MongoDB.Driver.Tests/Jira/CSharp3397Tests.cs b/tests/MongoDB.Driver.Tests/Jira/CSharp3397Tests.cs index 49a24a71359..a7212d38c57 100644 --- a/tests/MongoDB.Driver.Tests/Jira/CSharp3397Tests.cs +++ b/tests/MongoDB.Driver.Tests/Jira/CSharp3397Tests.cs @@ -14,7 +14,6 @@ */ using System; -using System.Linq; using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; @@ -24,6 +23,7 @@ namespace MongoDB.Driver.Tests.Jira { + [Trait("Category", "Integration")] public class CSharp3397Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp1486Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp1486Tests.cs index efeb0603e23..ba4e874cab2 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp1486Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp1486Tests.cs @@ -19,6 +19,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp1486Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp1771Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp1771Tests.cs index 4136ece2276..f7accf57861 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp1771Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp1771Tests.cs @@ -18,6 +18,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp1771Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2071Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2071Tests.cs index 9f2f9bbbb4c..e446c2b1e70 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2071Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2071Tests.cs @@ -18,6 +18,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp2071Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2308Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2308Tests.cs index 2fe0d21cf63..0aee509d5ea 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2308Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2308Tests.cs @@ -21,6 +21,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp2308Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2422Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2422Tests.cs index 0f6b5f62fb9..034b955b29a 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2422Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2422Tests.cs @@ -21,6 +21,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp2422Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2708Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2708Tests.cs index 74e078c217d..304de253976 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2708Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2708Tests.cs @@ -14,11 +14,11 @@ */ using System.Linq; -using MongoDB.Driver.Linq; using Xunit; namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp2708Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2723Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2723Tests.cs index ad64d2efd43..63065869144 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2723Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp2723Tests.cs @@ -17,11 +17,11 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Driver.Linq; using Xunit; namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp2723Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3283Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3283Tests.cs index 0439713d106..cc75b83ee78 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3283Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3283Tests.cs @@ -23,6 +23,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp3283Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3425Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3425Tests.cs index 41f427d01a6..64afec1a437 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3425Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3425Tests.cs @@ -18,6 +18,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp3425Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3524Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3524Tests.cs index d5dbc8274ba..55db449fe15 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3524Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3524Tests.cs @@ -24,6 +24,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp3524Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3630Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3630Tests.cs index 2b0f1583f1a..de33370eee2 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3630Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3630Tests.cs @@ -18,6 +18,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp3630Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3713Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3713Tests.cs index 2673013b576..448a6361063 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3713Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3713Tests.cs @@ -19,6 +19,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp3713Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3865Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3865Tests.cs index 345b07748ff..9fa2efa1ba8 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3865Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3865Tests.cs @@ -14,11 +14,11 @@ */ using System.Linq; -using MongoDB.Driver.Linq; using Xunit; namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp3865Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3910Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3910Tests.cs index 86923f5ae85..691de55fafe 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3910Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3910Tests.cs @@ -18,6 +18,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp3910Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3933Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3933Tests.cs index 02315b1affc..212ee4a4c1d 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3933Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3933Tests.cs @@ -16,11 +16,11 @@ using System.Linq; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Driver.Linq; using Xunit; namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp3933Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3940Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3940Tests.cs index 38bc73edba0..13c1b6456be 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3940Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp3940Tests.cs @@ -19,6 +19,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira { + [Trait("Category", "Integration")] public class CSharp3940Tests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Linq3IntegrationTest.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Linq3IntegrationTest.cs index fd1440ed500..b198cf4e9a1 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Linq3IntegrationTest.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Linq3IntegrationTest.cs @@ -18,12 +18,13 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization; -using MongoDB.Driver.Linq; using MongoDB.Driver.Linq.Linq3Implementation; using MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToExecutableQueryTranslators; +using Xunit; namespace MongoDB.Driver.Tests.Linq.Linq3Implementation { + [Trait("Category", "Integration")] public abstract class Linq3IntegrationTest { protected void AssertStages(IEnumerable stages, params string[] expectedStages) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/MongoQueryProviderTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/MongoQueryProviderTests.cs index 98f8f5d2155..65890c4b457 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/MongoQueryProviderTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/MongoQueryProviderTests.cs @@ -24,6 +24,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation { + [Trait("Category", "Integration")] public class MongoQueryProviderTests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/IntegrationTestBase.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/IntegrationTestBase.cs index e422523ff9d..f6e8c0bd3ad 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/IntegrationTestBase.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/IntegrationTestBase.cs @@ -18,9 +18,11 @@ using System.Collections.Generic; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; +using Xunit; namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationWithLinq2Tests { + [Trait("Category", "Integration")] public abstract class IntegrationTestBase { protected static IMongoCollection __collection; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableEnumComparedToEnumTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableEnumComparedToEnumTests.cs index 0020e737169..1c3ff6f6226 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableEnumComparedToEnumTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableEnumComparedToEnumTests.cs @@ -22,6 +22,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationWithLinq2Tests { + [Trait("Category", "Integration")] public class MongoQueryableEnumComparedToEnumTests { private static readonly IMongoClient __client; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableEnumComparedToEnumWithStringRepresentationTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableEnumComparedToEnumWithStringRepresentationTests.cs index cad6a9933d2..caf157ce7d3 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableEnumComparedToEnumWithStringRepresentationTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableEnumComparedToEnumWithStringRepresentationTests.cs @@ -23,6 +23,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationWithLinq2Tests { + [Trait("Category", "Integration")] public class MongoQueryableEnumComparedToEnumWithStringRepresentationTests { private static readonly IMongoClient __client; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntArrayComparedToEnumerableIntTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntArrayComparedToEnumerableIntTests.cs index fae8b42b343..c8280dc4756 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntArrayComparedToEnumerableIntTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntArrayComparedToEnumerableIntTests.cs @@ -23,6 +23,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationWithLinq2Tests { + [Trait("Category", "Integration")] public class MongoQueryableIntArrayComparedToEnumerableIntTests { private static readonly IMongoClient __client; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToDoubleTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToDoubleTests.cs index cc9d25626e5..6cf5a074a35 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToDoubleTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToDoubleTests.cs @@ -22,6 +22,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationWithLinq2Tests { + [Trait("Category", "Integration")] public class MongoQueryableIntComparedToDoubleTests { private static readonly IMongoClient __client; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToDoubleWithStringRepresentationTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToDoubleWithStringRepresentationTests.cs index ac02c947b84..511d103038b 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToDoubleWithStringRepresentationTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToDoubleWithStringRepresentationTests.cs @@ -23,6 +23,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationWithLinq2Tests { + [Trait("Category", "Integration")] public class MongoQueryableIntComparedToDoubleWithStringRepresentationTests { private static readonly IMongoClient __client; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToNullableIntTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToNullableIntTests.cs index 69d9d98ac0f..c98b57fb299 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToNullableIntTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToNullableIntTests.cs @@ -22,6 +22,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationWithLinq2Tests { + [Trait("Category", "Integration")] public class MongoQueryableIntComparedToNullableIntTests { private static readonly IMongoClient __client; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToNullableIntWithStringRepresentationTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToNullableIntWithStringRepresentationTests.cs index d9367c2df9f..96a07f29d6a 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToNullableIntWithStringRepresentationTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableIntComparedToNullableIntWithStringRepresentationTests.cs @@ -23,6 +23,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationWithLinq2Tests { + [Trait("Category", "Integration")] public class MongoQueryableIntComparedToNullableIntWithStringRepresentationTests { private static readonly IMongoClient __client; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableNullableEnumComparedToNullableEnumTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableNullableEnumComparedToNullableEnumTests.cs index 0d28da7db01..945210f690c 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableNullableEnumComparedToNullableEnumTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableNullableEnumComparedToNullableEnumTests.cs @@ -22,6 +22,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationWithLinq2Tests { + [Trait("Category", "Integration")] public class MongoQueryableNullableEnumComparedToNullableEnumTests { private static readonly IMongoClient __client; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableNullableEnumComparedToNullableEnumWithStringRepresentationTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableNullableEnumComparedToNullableEnumWithStringRepresentationTests.cs index e8d4310a731..dd8ccf9e5d8 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableNullableEnumComparedToNullableEnumWithStringRepresentationTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableNullableEnumComparedToNullableEnumWithStringRepresentationTests.cs @@ -23,6 +23,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationWithLinq2Tests { + [Trait("Category", "Integration")] public class MongoQueryableNullableEnumComparedToNullableEnumWithStringRepresentationTests { private static readonly IMongoClient __client; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableWithDotNotationTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableWithDotNotationTests.cs index 0e4e4af8c0f..f43d71146fe 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableWithDotNotationTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/MongoQueryableWithDotNotationTests.cs @@ -24,6 +24,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationWithLinq2Tests { + [Trait("Category", "Integration")] public class MongoQueryableWithDotNotationTests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/LegacyPredicateTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/LegacyPredicateTranslatorTests.cs index 361855cc07a..cd49af1955f 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/LegacyPredicateTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/LegacyPredicateTranslatorTests.cs @@ -22,7 +22,6 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; -using MongoDB.Driver; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Optimizers; using MongoDB.Driver.Linq.Linq3Implementation.Misc; using MongoDB.Driver.Linq.Linq3Implementation.Translators; @@ -31,6 +30,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationWithLinq2Tests.Translators { + [Trait("Category", "Integration")] public class LegacyPredicateTranslatorTests { private static IMongoDatabase __database; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/PredicateTranslatorValidationTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/PredicateTranslatorValidationTests.cs index 7bdb46a2219..12d51f85ce8 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/PredicateTranslatorValidationTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/PredicateTranslatorValidationTests.cs @@ -19,13 +19,13 @@ using System.Linq.Expressions; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Driver; using MongoDB.Driver.Linq.Linq3Implementation; using MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToExecutableQueryTranslators; using Xunit; namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationWithLinq2Tests.Translators { + [Trait("Category", "Integration")] public class PredicateTranslatorValidationTests { private IMongoCollection _collection; diff --git a/tests/MongoDB.Driver.Tests/ListDatabasesTests.cs b/tests/MongoDB.Driver.Tests/ListDatabasesTests.cs index 9840f441ec0..c5bf7d955a1 100644 --- a/tests/MongoDB.Driver.Tests/ListDatabasesTests.cs +++ b/tests/MongoDB.Driver.Tests/ListDatabasesTests.cs @@ -23,6 +23,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class ListDatabasesTests { private string _databaseName = $"authorizedDatabases{Guid.NewGuid()}"; diff --git a/tests/MongoDB.Driver.Tests/LoggingTests.cs b/tests/MongoDB.Driver.Tests/LoggingTests.cs index 04f83dbca0e..118d2404f98 100644 --- a/tests/MongoDB.Driver.Tests/LoggingTests.cs +++ b/tests/MongoDB.Driver.Tests/LoggingTests.cs @@ -21,12 +21,12 @@ using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.TestHelpers.Logging; -using MongoDB.Driver.Linq; using Xunit; using Xunit.Abstractions; namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class LoggingTests : LoggableTestClass { public LoggingTests(ITestOutputHelper output) : base(output, includeAllCategories: true) @@ -102,7 +102,7 @@ public void Prose_tests_truncation_limit_1(int? maxDocumentSize) : new LoggingSettings(LoggerFactory, maxDocumentSize.Value); using (var client = DriverTestConfiguration.CreateMongoClient(loggingSettings)) { - + var db = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); try diff --git a/tests/MongoDB.Driver.Tests/MongoClientSettingsTests.cs b/tests/MongoDB.Driver.Tests/MongoClientSettingsTests.cs index ddcffdadf19..6c188b5c6a4 100644 --- a/tests/MongoDB.Driver.Tests/MongoClientSettingsTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoClientSettingsTests.cs @@ -1110,6 +1110,7 @@ public void TestServersWithSrvMaxHosts() } [Fact] + [Trait("Category", "Integration")] public void TestSocketConfigurator() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Tests/MongoClientTests.cs b/tests/MongoDB.Driver.Tests/MongoClientTests.cs index d1f06516a2f..36d6bca9231 100644 --- a/tests/MongoDB.Driver.Tests/MongoClientTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoClientTests.cs @@ -28,6 +28,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class MongoClientTests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs b/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs index 818c29ade41..d89660af55e 100644 --- a/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs @@ -3805,6 +3805,7 @@ public void Watch_should_throw_when_pipeline_is_null( } [Fact] + [Trait("Category", "Integration")] public void Watch_should_support_full_document_with_duplicate_elements() { RequireServer.Check().ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded); diff --git a/tests/MongoDB.Driver.Tests/MongoCredentialTests.cs b/tests/MongoDB.Driver.Tests/MongoCredentialTests.cs index 8b1c38ee667..e44f90e0cdc 100644 --- a/tests/MongoDB.Driver.Tests/MongoCredentialTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoCredentialTests.cs @@ -20,6 +20,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class MongoCredentialTests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/MongoDatabasTests.cs b/tests/MongoDB.Driver.Tests/MongoDatabasTests.cs index 9b35886d6b9..68cb5526beb 100644 --- a/tests/MongoDB.Driver.Tests/MongoDatabasTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoDatabasTests.cs @@ -380,6 +380,7 @@ public void AggregateToCollection_should_throw_when_last_stage_is_not_an_output_ [Theory] [ParameterAttributeData] + [Trait("Category", "Integration")] public void CreateCollection_should_execute_a_CreateCollectionOperation_when_options_is_generic( [Values(false, true)] bool usingSession, [Values(false, true)] bool clustered, @@ -881,6 +882,7 @@ public void ListCollectionNames_should_execute_a_ListCollectionsOperation( [Theory] [ParameterAttributeData] + [Trait("Category", "Integration")] public void ListCollectionNames_should_return_expected_result( [Values(0, 1, 2, 10)] int numberOfCollections, [Values(null, false, true)] bool? usingAuthorizedCollections, diff --git a/tests/MongoDB.Driver.Tests/MongoIndexManagerTests.cs b/tests/MongoDB.Driver.Tests/MongoIndexManagerTests.cs index ac5fbef4289..1c0228246a1 100644 --- a/tests/MongoDB.Driver.Tests/MongoIndexManagerTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoIndexManagerTests.cs @@ -22,6 +22,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class MongoIndexManagerTests { [Theory] diff --git a/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs b/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs index 04de648d449..a1adc1f8713 100644 --- a/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs +++ b/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs @@ -954,6 +954,7 @@ public class UpdateTestCases : IValueGenerator } } + [Trait("Category", "Integration")] public class OfTypeCollectionIntegrationTests : IDisposable { private readonly IMongoCollection _docsCollection; diff --git a/tests/MongoDB.Driver.Tests/PinnedShardRouterTests.cs b/tests/MongoDB.Driver.Tests/PinnedShardRouterTests.cs index 7f13da6299a..0bc0f122ebc 100644 --- a/tests/MongoDB.Driver.Tests/PinnedShardRouterTests.cs +++ b/tests/MongoDB.Driver.Tests/PinnedShardRouterTests.cs @@ -26,12 +26,12 @@ using MongoDB.Driver.Core.Servers; using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; -using MongoDB.Driver.TestHelpers; using Xunit; using Xunit.Abstractions; namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class PinnedShardRouterTests : LoggableTestClass { private static readonly HashSet __commandsToNotCapture = new HashSet diff --git a/tests/MongoDB.Driver.Tests/PipelineDefinitionBuilderTests.cs b/tests/MongoDB.Driver.Tests/PipelineDefinitionBuilderTests.cs index 3c556553c7d..0c64431d746 100644 --- a/tests/MongoDB.Driver.Tests/PipelineDefinitionBuilderTests.cs +++ b/tests/MongoDB.Driver.Tests/PipelineDefinitionBuilderTests.cs @@ -181,6 +181,7 @@ public void Lookup_should_throw_when_pipeline_is_null() } [Fact] + [Trait("Category", "Integration")] public void Merge_should_add_expected_stage() { var pipeline = new EmptyPipelineDefinition(); @@ -233,15 +234,15 @@ public void RankFusion_with_named_pipelines_should_add_expected_stage() stages.Count.Should().Be(1); stages[0].Should().Be(""" { - $rankFusion: { - "input" : { - "pipelines" : { + $rankFusion: { + "input" : { + "pipelines" : { "p1" : [{ "$match" : { "x" : 1 } }, { "$sort" : { "y" : 1 } }], - "p2" : [{ "$match" : { "x" : 2 } }, { "$sort" : { "y" : -1 } }] - } + "p2" : [{ "$match" : { "x" : 2 } }, { "$sort" : { "y" : -1 } }] + } }, "combination" : { "weights" : { "p1" : 0.3, "p2" : 0.7 } } - "scoreDetails" : true + "scoreDetails" : true } } """); @@ -260,12 +261,12 @@ public void RankFusion_without_named_pipelines_should_add_expected_stage() stages.Count.Should().Be(1); stages[0].Should().Be(""" { - $rankFusion: { - "input" : { - "pipelines" : { + $rankFusion: { + "input" : { + "pipelines" : { "pipeline1" : [{ "$match" : { "x" : 1 } }, { "$sort" : { "y" : 1 } }], - "pipeline2" : [{ "$match" : { "x" : 2 } }, { "$sort" : { "y" : -1 } }] - } + "pipeline2" : [{ "$match" : { "x" : 2 } }, { "$sort" : { "y" : -1 } }] + } } } } @@ -286,13 +287,13 @@ public void RankFusion_using_pipeline_weight_tuples_should_add_expected_stage() stages.Count.Should().Be(1); stages[0].Should().Be(""" { - $rankFusion: { - "input" : { - "pipelines" : { + $rankFusion: { + "input" : { + "pipelines" : { "pipeline1" : [{ "$match" : { "x" : 1 } }, { "$sort" : { "y" : 1 } }], - "pipeline2" : [{ "$match" : { "x" : 2 } }, { "$sort" : { "y" : -1 } }], - "pipeline3" : [{ "$match" : { "x" : 3 } }, { "$sort" : { "y" : 1 } }] - } + "pipeline2" : [{ "$match" : { "x" : 2 } }, { "$sort" : { "y" : -1 } }], + "pipeline3" : [{ "$match" : { "x" : 3 } }, { "$sort" : { "y" : 1 } }] + } }, "combination" : { "weights" : { "pipeline1" : 0.3, "pipeline2" : 0.7 } } } diff --git a/tests/MongoDB.Driver.Tests/PipelineStageDefinitionBuilderTests.cs b/tests/MongoDB.Driver.Tests/PipelineStageDefinitionBuilderTests.cs index d78985a8932..1462d3db2be 100644 --- a/tests/MongoDB.Driver.Tests/PipelineStageDefinitionBuilderTests.cs +++ b/tests/MongoDB.Driver.Tests/PipelineStageDefinitionBuilderTests.cs @@ -27,6 +27,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class PipelineStageDefinitionBuilderTests { // public methods @@ -150,15 +151,15 @@ public void GeoNear_with_array_should_return_the_expected_result() var stage = RenderStage(result); stage.Document.Should().Be(""" - { - "$geoNear" : { - "near" : [34.0, 67.0], - "distanceField" : "calculatedDistance", - "maxDistance" : 3.0, - "query" : { "testfield" : "testvalue" }, - "includeLocs" : "usedLocation", - "spherical" : true - } + { + "$geoNear" : { + "near" : [34.0, 67.0], + "distanceField" : "calculatedDistance", + "maxDistance" : 3.0, + "query" : { "testfield" : "testvalue" }, + "includeLocs" : "usedLocation", + "spherical" : true + } } """); } @@ -179,15 +180,15 @@ public void GeoNear_with_geojson_point_should_return_the_expected_result() var stage = RenderStage(result); stage.Document.Should().Be(""" - { - "$geoNear" : { - "near" : { "type" : "Point", "coordinates" : [34.0, 67.0] }, - "distanceField" : "calculatedDistance", - "maxDistance" : 3.0, - "query" : { "testfield" : "testvalue" }, - "includeLocs" : "usedLocation", - "spherical" : true - } + { + "$geoNear" : { + "near" : { "type" : "Point", "coordinates" : [34.0, 67.0] }, + "distanceField" : "calculatedDistance", + "maxDistance" : 3.0, + "query" : { "testfield" : "testvalue" }, + "includeLocs" : "usedLocation", + "spherical" : true + } } """); } diff --git a/tests/MongoDB.Driver.Tests/ReadPreferenceOnStandaloneTests.cs b/tests/MongoDB.Driver.Tests/ReadPreferenceOnStandaloneTests.cs index 63231820663..fb507d13c37 100644 --- a/tests/MongoDB.Driver.Tests/ReadPreferenceOnStandaloneTests.cs +++ b/tests/MongoDB.Driver.Tests/ReadPreferenceOnStandaloneTests.cs @@ -29,6 +29,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class ReadPreferenceOnStandaloneTests : LoggableTestClass { public ReadPreferenceOnStandaloneTests(ITestOutputHelper output) : base(output) diff --git a/tests/MongoDB.Driver.Tests/RetryableWritesTests.cs b/tests/MongoDB.Driver.Tests/RetryableWritesTests.cs index 6f2fc4bad44..58fdbdfa2c3 100644 --- a/tests/MongoDB.Driver.Tests/RetryableWritesTests.cs +++ b/tests/MongoDB.Driver.Tests/RetryableWritesTests.cs @@ -30,6 +30,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "Integration")] public class RetryableWritesTests : LoggableTestClass { // public constructors diff --git a/tests/MongoDB.Driver.Tests/Samples/AggregationSample.cs b/tests/MongoDB.Driver.Tests/Samples/AggregationSample.cs index ed26da058fe..54dc53dfc90 100644 --- a/tests/MongoDB.Driver.Tests/Samples/AggregationSample.cs +++ b/tests/MongoDB.Driver.Tests/Samples/AggregationSample.cs @@ -23,6 +23,7 @@ namespace MongoDB.Driver.Tests.Samples { + [Trait("Category", "Integration")] public class AggregationSample { private static IMongoCollection __collection; diff --git a/tests/MongoDB.Driver.Tests/Search/MongoQueryableTests.cs b/tests/MongoDB.Driver.Tests/Search/MongoQueryableTests.cs index 3597958e860..64ef9a47bed 100644 --- a/tests/MongoDB.Driver.Tests/Search/MongoQueryableTests.cs +++ b/tests/MongoDB.Driver.Tests/Search/MongoQueryableTests.cs @@ -22,6 +22,7 @@ namespace MongoDB.Driver.Tests.Search { + [Trait("Category", "Integration")] public class MongoQueryableTests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Search/VectorSearchTests.cs b/tests/MongoDB.Driver.Tests/Search/VectorSearchTests.cs index d39679f25b6..358d928d0bf 100644 --- a/tests/MongoDB.Driver.Tests/Search/VectorSearchTests.cs +++ b/tests/MongoDB.Driver.Tests/Search/VectorSearchTests.cs @@ -28,6 +28,7 @@ namespace MongoDB.Driver.Tests.Search { [Trait("Category", "AtlasSearch")] + [Trait("Category", "Integration")] public class VectorSearchTests : LoggableTestClass { private readonly IMongoClient _mongoClient; diff --git a/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs index fe8d7683961..17e9f36d60f 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs @@ -32,6 +32,7 @@ namespace MongoDB.Driver.Tests.Specifications { + [Trait("Category", "Integration")] public class UnifiedTestSpecRunner : LoggableTestClass { public UnifiedTestSpecRunner(ITestOutputHelper testOutputHelper) diff --git a/tests/MongoDB.Driver.Tests/Specifications/change-streams/prose-tests/ChangeStreamProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/change-streams/prose-tests/ChangeStreamProseTests.cs index dcbfbc82cfe..69beb58fa4f 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/change-streams/prose-tests/ChangeStreamProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/change-streams/prose-tests/ChangeStreamProseTests.cs @@ -25,6 +25,7 @@ namespace MongoDB.Driver.Tests.Specifications.change_streams.prose_tests { + [Trait("Category", "Integration")] public class ChangeStreamProseTests : LoggableTestClass { public ChangeStreamProseTests(ITestOutputHelper testOutputHelper) diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs index 29cf6333a79..e4304e3d494 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs @@ -29,6 +29,7 @@ namespace MongoDB.Driver.Tests.Specifications.client_side_encryption { [Trait("Category", "CSFLE")] [Trait("Category", "Serverless")] + [Trait("Category", "Integration")] public class ClientSideEncryptionTestRunner : MongoClientJsonDrivenTestRunnerBase { #region static diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs index f050c834cca..ceafb5aeae4 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs @@ -55,6 +55,7 @@ namespace MongoDB.Driver.Tests.Specifications.client_side_encryption.prose_tests { [Trait("Category", "CSFLE")] + [Trait("Category", "Integration")] public class ClientEncryptionProseTests : LoggableTestClass { #region static diff --git a/tests/MongoDB.Driver.Tests/Specifications/connection-monitoring-and-pooling/ConnectionMonitoringAndPoolingTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/connection-monitoring-and-pooling/ConnectionMonitoringAndPoolingTestRunner.cs index f7b1b28c512..626ef59450a 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/connection-monitoring-and-pooling/ConnectionMonitoringAndPoolingTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/connection-monitoring-and-pooling/ConnectionMonitoringAndPoolingTestRunner.cs @@ -44,6 +44,7 @@ namespace MongoDB.Driver.Tests.Specifications.connection_monitoring_and_pooling { [Trait("Category", "Pool")] + [Trait("Category", "Integration")] public class ConnectionMonitoringAndPoolingTestRunner { #region static diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/CrudTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/crud/CrudTestRunner.cs index acdf4c75f80..5659538735d 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/crud/CrudTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/crud/CrudTestRunner.cs @@ -32,6 +32,7 @@ namespace MongoDB.Driver.Tests.Specifications.crud { [Trait("Category", "Serverless")] + [Trait("Category", "Integration")] public class CrudTestRunner : LoggableTestClass { #region static diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/prose-tests/CrudProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/crud/prose-tests/CrudProseTests.cs index 41987fcfc3e..94902ab9839 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/crud/prose-tests/CrudProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/crud/prose-tests/CrudProseTests.cs @@ -34,6 +34,7 @@ namespace MongoDB.Driver.Tests.Specifications.crud.prose_tests { [Trait("Category", "Serverless")] + [Trait("Category", "Integration")] public class CrudProseTests : LoggableTestClass { // public constructors diff --git a/tests/MongoDB.Driver.Tests/Specifications/initial-dns-seedlist-discovery/InitialDnsSeedlistDiscoveryTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/initial-dns-seedlist-discovery/InitialDnsSeedlistDiscoveryTestRunner.cs index f5e2fdac6d5..64f8b1d7482 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/initial-dns-seedlist-discovery/InitialDnsSeedlistDiscoveryTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/initial-dns-seedlist-discovery/InitialDnsSeedlistDiscoveryTestRunner.cs @@ -32,6 +32,7 @@ namespace MongoDB.Driver.Tests.Specifications.initial_dns_seedlist_discovery { [Trait("Category", "ConnectionString")] [Trait("Category", "SupportLoadBalancing")] + [Trait("Category", "Integration")] public class InitialDnsSeedlistDiscoveryTestRunner { [Theory] diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsProseTests.cs index 9738aab1c95..f4aa9cc83ac 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsProseTests.cs @@ -33,6 +33,7 @@ namespace MongoDB.Driver.Tests.Specifications.retryable_reads { + [Trait("Category", "Integration")] public class RetryableReadsProseTests { [Theory] diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/MMapV1Tests.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/MMapV1Tests.cs index daf40a50152..3e25789963d 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/MMapV1Tests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/MMapV1Tests.cs @@ -25,6 +25,7 @@ namespace MongoDB.Driver.Tests.Specifications.retryable_writes.prose_tests { + [Trait("Category", "Integration")] public class MMapV1Tests : LoggableTestClass { public MMapV1Tests(ITestOutputHelper output) : base(output) diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/PoolClearRetryability.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/PoolClearRetryability.cs index b3dca6097a8..9054e532708 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/PoolClearRetryability.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/PoolClearRetryability.cs @@ -34,6 +34,7 @@ namespace MongoDB.Driver.Tests.Specifications.retryable_writes.prose_tests { + [Trait("Category", "Integration")] public class PoolClearRetryability { [Theory] diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/RetryWriteOnOtherMongos.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/RetryWriteOnOtherMongos.cs index 9fcb7357c4c..75f89943f40 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/RetryWriteOnOtherMongos.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/RetryWriteOnOtherMongos.cs @@ -28,6 +28,7 @@ namespace MongoDB.Driver.Tests.Specifications.retryable_writes.prose_tests { + [Trait("Category", "Integration")] public class RetryWriteOnOtherMongos { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs index 2d9d3251d7b..c95e14ee7c5 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs @@ -32,13 +32,13 @@ using MongoDB.Driver.Core.Servers; using MongoDB.Driver.Core.TestHelpers; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; -using MongoDB.Driver.TestHelpers; using Moq; using Xunit; namespace MongoDB.Driver.Tests.Specifications.server_discovery_and_monitoring { [Trait("Category", "SDAM")] + [Trait("Category", "Integration")] public class ServerDiscoveryAndMonitoringProseTests { [Fact] diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/prose-tests/ServerDiscoveryProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/prose-tests/ServerDiscoveryProseTests.cs index 38fbadcce4c..928823a8570 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/prose-tests/ServerDiscoveryProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/prose-tests/ServerDiscoveryProseTests.cs @@ -30,6 +30,7 @@ namespace MongoDB.Driver.Tests.Specifications.server_discovery_and_monitoring.prose_tests { + [Trait("Category", "Integration")] public class ServerDiscoveryProseTests : LoggableTestClass { // public constructors diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsProseTests.cs index a576e48ab48..a2f6f05f091 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsProseTests.cs @@ -34,6 +34,7 @@ namespace MongoDB.Driver.Tests.Specifications.sessions { [Trait("Category", "Serverless")] + [Trait("Category", "Integration")] public class SessionsProseTests : LoggableTestClass { public SessionsProseTests(ITestOutputHelper output) : base(output) diff --git a/tests/MongoDB.Driver.Tests/Specifications/uuid/prose-tests/ImplicitEncodingTests.cs b/tests/MongoDB.Driver.Tests/Specifications/uuid/prose-tests/ImplicitEncodingTests.cs index 5a13163a1f3..c3945fa0fdf 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/uuid/prose-tests/ImplicitEncodingTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/uuid/prose-tests/ImplicitEncodingTests.cs @@ -22,6 +22,7 @@ namespace MongoDB.Driver.Tests.Specifications.uuid.prose_tests { + [Trait("Category", "Integration")] public class ImplicitEncodingTests { [Fact] diff --git a/tests/MongoDB.TestHelpers/MongoDB.TestHelpers.csproj b/tests/MongoDB.TestHelpers/MongoDB.TestHelpers.csproj index 9c2d724e529..f26db14e0fb 100644 --- a/tests/MongoDB.TestHelpers/MongoDB.TestHelpers.csproj +++ b/tests/MongoDB.TestHelpers/MongoDB.TestHelpers.csproj @@ -2,6 +2,7 @@ + false ..\..\MongoDBLegacyTest.ruleset diff --git a/tests/MongoDB.TestHelpers/XunitExtensions/IntegrationTestAttribute.cs b/tests/MongoDB.TestHelpers/XunitExtensions/IntegrationTestAttribute.cs deleted file mode 100644 index 06b49d22336..00000000000 --- a/tests/MongoDB.TestHelpers/XunitExtensions/IntegrationTestAttribute.cs +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright 2010-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -namespace MongoDB.TestHelpers.XunitExtensions -{ - public sealed class IntegrationTestAttribute : CategoryAttribute - { - public IntegrationTestAttribute() - : base("Integration") - { - } - } -} diff --git a/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LibmongocryptTests.cs b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LibmongocryptTests.cs index 627a5ea99d7..1ff18a30050 100644 --- a/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LibmongocryptTests.cs +++ b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LibmongocryptTests.cs @@ -25,6 +25,7 @@ namespace MongoDB.Driver.SmokeTests.Sdk { + [Trait("Category", "Integration")] public class LibmongocryptTests { private const string LocalMasterKey = "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk"; diff --git a/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LoggingTests.cs b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LoggingTests.cs index a51cfd21a6e..21e78fea664 100644 --- a/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LoggingTests.cs +++ b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LoggingTests.cs @@ -23,6 +23,7 @@ namespace MongoDB.Driver.SmokeTests.Sdk { + [Trait("Category", "Integration")] public sealed class LoggingTests { private readonly ITestOutputHelper _output; From 341001536e7d0d8c17e2dd6bc87d9ecd4fb998f9 Mon Sep 17 00:00:00 2001 From: Adelin Owona <51498470+adelinowona@users.noreply.github.com> Date: Fri, 23 May 2025 23:44:51 -0400 Subject: [PATCH 07/24] CSHARP-5539: Update Atlas Search tests for rankFusion (#1693) --- .../Search/AtlasSearchTests.cs | 85 +++++++------------ 1 file changed, 31 insertions(+), 54 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Search/AtlasSearchTests.cs b/tests/MongoDB.Driver.Tests/Search/AtlasSearchTests.cs index b014e9152a8..450ea20fe26 100644 --- a/tests/MongoDB.Driver.Tests/Search/AtlasSearchTests.cs +++ b/tests/MongoDB.Driver.Tests/Search/AtlasSearchTests.cs @@ -18,7 +18,6 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.Logging; @@ -160,7 +159,7 @@ public void EqualsNull() [Fact] public void EqualsArrayField() { - var results = GetSynonymTestCollection().Aggregate() + var results = GetMoviesCollection().Aggregate() .Search(Builders.Search.Equals(p => p.Genres, "family")) .Limit(3) .ToList(); @@ -179,7 +178,7 @@ public void EqualsArrayField() [Fact] public void EqualsStringField() { - var results = GetSynonymTestCollection().Aggregate() + var results = GetMoviesCollection().Aggregate() .Search(Builders.Search.Equals(p => p.Title, "a corner in wheat")) .ToList(); @@ -279,7 +278,7 @@ public void GeoWithin(string geometryType) [Fact] public void In() { - var results = GetSynonymTestCollection() + var results = GetMoviesCollection() .Aggregate() .Search( Builders.Search.In(x => x.Runtime, new[] { 31, 231 }), @@ -425,7 +424,7 @@ public void PhraseAnalyzerPath() public void PhraseSynonym() { var result = - GetSynonymTestCollection().Aggregate() + GetMoviesCollection().Aggregate() .Search( Builders.Search.Phrase("plot", "automobile race", new SearchPhraseOptions { Synonyms = "transportSynonyms" }), indexName: "synonyms-tests") @@ -474,7 +473,7 @@ public void Range() [Fact] public void RangeString() { - var results = GetSynonymTestCollection().Aggregate() + var results = GetMoviesCollection().Aggregate() .Search(Builders.Search.Range(p => p.Title, SearchRangeV2Builder.Gt("city").Lt("country"))) .Limit(5) .Project(Builders.Projection.Include(p => p.Title)) @@ -487,62 +486,35 @@ public void RangeString() results[4].Title.Should().Be("Come and Get It"); } - // TODO: Once we have an Atlas cluster running server 8.1, update this test to retrieve actual results from the server instead of merely validating the syntax. [Fact] public void RankFusion() { const int limit = 5; - var vector = new[] { 1.0, 2.0, 3.0 }; - var vectorOptions = new VectorSearchOptions() + var vector = new[] { -0.00028408386, -0.030921403, 0.0017461961, -0.007926553, -0.008247016, 0.029273309, 0.0027844305, 0.0088290805, 0.0020862792, 0.001850837, 0.0004868257, 0.004914855, 0.030529, -0.033118863, 0.022419326, 0.0031359587, 0.030842923, -0.016101629, 0.018940013, -0.006186897, -0.008390897, 0.007514529, 0.008175075, -0.012380334, 0.007200606, 0.0015352791, 0.0071482854, -0.011484345, 0.007194066, -0.006736262, 0.0009049808, -0.01856069, 0.008959882, -0.02718049, -0.030031955, -0.012609236, -0.011432025, -0.000060597744, 0.0390834, -0.00019783681, 0.0071025053, 0.01747504, -0.015918506, -0.0062261373, -0.0049966057, 0.008534778, 0.009247645, -0.007959253, -0.04015597, 0.013838767, 0.013969569, 0.010934981, -0.0040025166, 0.0022285255, -0.0067820423, -0.008194695, -0.0096335085, 0.006209787, 0.010261354, -0.006445229, -0.0066839415, -0.0025702436, -0.0028007806, 0.0009164259, -0.012151431, -0.00014674259, 0.011314304, -0.019737901, 0.0068997634, 0.007331407, 0.036336575, -0.0021680298, 0.0024606977, -0.0007745884, 0.00985587, -0.0049573653, -0.022066163, -0.0065040896, -0.010745319, -0.008802921, 0.00021173444, -0.028880905, -0.021098234, 0.03481928, 0.03822011, 0.003809585, -0.011693628, 0.012726957, -0.012197211, 0.0019865432, -0.0028776263, -0.008436677, -0.0021631247, 0.0118375085, -0.0044962913, 0.002622564, -0.011360084, 0.00865904, -0.009659668, -0.027677534, -0.019397818, 0.0040875375, 0.011386245, -0.0011322479, 0.003714754, 0.005578671, 0.025218472, 0.012112191, 0.014623574, -0.002947932, 0.0041954485, 0.009456927, 0.018142127, -0.055878274, -0.014335811, -0.03162773, 0.0075733894, 0.015840026, 0.005258208, -0.015879266, 0.033354305, -0.004542072, -0.006638161, -0.0075930096, -0.011366624, 0.019332416, -0.019515539, -0.022445485, 0.005876244, -0.016559431, 0.018220607, -0.0039894367, 0.031209165, -0.0049737156, -0.020195706, 0.0175012, -0.024669107, 0.0014339081, -0.005912214, -0.015800785, 0.0117197875, 0.008161995, -0.00982971, -0.0023348015, -0.008292796, 0.035420965, -0.00040343995, 0.0022628608, 0.00032904677, -0.009273805, 0.01975098, -0.013420203, 0.016650993, -0.009143004, 0.024865309, 0.0035185523, 0.007305247, 0.024132822, -0.012635396, -0.0118375085, -0.00873098, 0.011706707, 0.0009687464, -0.012295313, 0.0057160123, 0.03508088, 0.003142499, 0.00035152823, 0.009476547, -0.028410021, -0.009908191, -0.0033109053, -0.009188784, 0.0148720965, -0.031183006, 0.022066163, 0.014021888, 0.022144644, -0.0108565, -0.008155455, -0.009005663, 0.025231551, 0.018966174, 0.009156084, -0.017017236, -0.017893605, -0.021320596, -0.008103134, 0.015840026, -0.013420203, 0.0027533653, 0.0054249796, -0.009339206, 0.0120271705, -0.6701207, -0.029901154, -0.012995099, -0.008050814, -0.005356309, 0.016977996, 0.021202875, -0.008207776, -0.006464849, 0.0027942406, -0.004453781, 0.027154328, -0.0054249796, -0.015905425, 0.0003684915, -0.011432025, 0.023544217, -0.024289783, 0.0054053594, -0.020313427, -0.03398215, 0.030031955, 0.020993592, -0.0015753369, -0.01965942, 0.0072725466, 0.02515307, -0.0078022913, -0.0094438465, 0.015683064, -0.022288524, 0.028880905, -0.0020388637, 0.009620428, 0.056296837, -0.02825306, -0.0049508256, 0.030319719, -0.020561948, 0.02189612, -0.009594268, -0.014885177, -0.004842914, -0.0040940777, 0.0069651636, -0.0061836266, 0.013564085, -0.0030018876, 0.023504976, 0.007946173, 0.00439492, 0.030895242, -0.007109045, -0.002619294, -0.0028759914, 0.008711359, 0.030215077, -0.013551004, 0.012406494, -0.010562196, -0.010503336, -0.0016023146, 0.005088167, 0.0026634394, -0.0011150802, 0.04154246, 0.015447621, 0.0417779, 0.016781794, -0.03377287, 0.037696905, 0.014466613, -0.0071417456, 0.017775884, -0.010137093, 0.0028972465, 0.024315942, -0.011026541, -0.008103134, -0.004470131, -0.0019244127, -0.0027975107, -0.025859397, -0.0050293063, 0.02404126, -0.012275693, -0.010719159, 0.01203371, 0.0030002524, 0.005892594, -0.008632879, 0.027154328, -0.0020241486, 0.000026620088, 0.007913472, -0.0123345535, 0.007821912, 0.0049508256, 0.0072463863, -0.039449643, 0.017592762, -0.034583837, 0.0058173835, -0.0136164045, 0.0045191813, -0.0022432406, -0.0027599053, 0.004872345, 0.061633524, -0.020287266, -0.008672119, 0.0023658667, 0.014440453, -0.004061377, -0.011785188, -0.02391046, 0.0074360482, 0.009411146, 0.030999884, -0.022602446, -0.0059449147, 0.016755633, 0.008417057, -0.014675895, 0.024760667, 0.015643824, -0.025950957, -0.01975098, -0.034688476, 0.013256702, -0.015853105, -0.004542072, 0.0031147036, -0.0036428133, 0.016167028, -0.0038815255, 0.013838767, -0.0061705466, 0.01963326, 0.0041006175, -0.008436677, 0.013276322, 0.011327384, -0.01757968, 0.00070837024, -0.029377948, -0.0011714882, -0.007501449, -0.04912893, 0.026591884, 0.017736642, -0.009371906, -0.015604583, 0.00028060944, 0.009581188, 0.00657276, -0.024826068, -0.006252297, -0.0038651754, -0.016729474, 0.01858685, 0.0048265643, -0.011628226, 0.011360084, -0.0044014603, -0.017736642, 0.016023146, 0.033171184, -0.008338576, -0.024211302, -0.009025283, -0.035525605, -0.02287713, 0.029037867, -0.002849831, -0.010627598, -0.0019881781, -0.022602446, -0.015316821, 0.008227396, -0.0026863297, 0.011700167, -0.00043777528, -0.001362785, 0.03604881, -0.0003721703, 0.00057225523, 0.017867444, -0.009757769, 0.037749227, -0.021516798, 0.027991457, 0.012864298, -0.011248903, -0.0052778283, 0.013498683, 0.008528238, 0.021137474, 0.008515158, 0.031339966, -0.001971828, -0.009947431, 0.028462341, -0.005150297, 0.020575028, -0.009522327, 0.0011649482, -0.028907064, 0.03508088, 0.003377941, 0.004287009, -0.024865309, 0.02064043, -0.0020192435, 0.006477929, 0.014911337, -0.005856624, 0.0015025787, -0.01629783, -0.0022285255, 0.019070815, -0.012426114, -0.01739656, -0.0031245137, -0.015539182, 0.02084971, 0.0005252486, 0.026539564, 0.006029935, -0.042222627, 0.013152061, 0.008031194, -0.015146779, 0.009221485, 0.022759408, -0.023060251, -0.0026634394, -0.015094458, 0.023295693, -0.014518933, 0.014924417, 0.026448002, 0.04350448, -0.010405235, -0.012184132, 0.014793616, 0.004015597, 0.00004401767, -0.019031575, 0.023714257, -0.017592762, 0.016873354, -0.012511135, 0.020391908, 0.0036297333, 0.012262613, 0.009430766, 0.0035545225, 0.022497807, 0.036336575, 0.0074229683, -0.013269782, 0.020221865, 0.021438317, 0.017972086, -0.013328643, 0.016821034, 0.009169164, 0.005117597, 0.024564466, -0.008037734, -0.01408729, -0.008070434, -0.00977085, 0.023884298, -0.0017412909, 0.006203247, -0.004578042, 0.013799527, -0.008279716, -0.013904167, -0.034296073, 0.020234946, 0.0153299, -0.011006921, -0.012753117, -0.015382221, 0.0047317334, -0.03144461, 0.013655645, -0.025715515, 0.012870838, -0.015055218, -0.011275063, 0.0025244632, -0.0068997634, 0.037696905, -0.005261478, 0.034296073, -0.021987682, 0.0059710746, 0.012746577, -0.02843618, -0.01739656, 0.011870209, -0.004018867, -0.024525225, -0.004571502, 0.0066774013, 0.005133947, -0.0029708222, -0.007534149, -0.0174358, 0.011190043, 0.0009000758, -0.012896998, -0.008541319, 0.013511764, 0.023884298, 0.009156084, -0.007095965, -0.032909583, -0.02733745, -0.002501573, 0.11887213, 0.017605841, 0.018390648, 0.013969569, 0.013577164, -0.004463591, -0.019358577, -0.018024405, 0.014100369, -0.0023462465, 0.005761793, -0.02281173, 0.002182745, 0.0037376443, 0.01747504, 0.00044840286, -0.022026923, -0.016101629, 0.013825687, -0.011772108, 0.0066970214, 0.0108892, -0.0031915493, 0.01631091, -0.021909202, -0.024172062, 0.008619799, 0.012569996, -0.008207776, -0.015042138, -0.015486862, -0.010542576, 0.0008943532, 0.021307515, -0.0015557167, 0.009476547, -0.00005768537, 0.011791728, 0.036388893, -0.0036657036, 0.023871219, 0.024747588, 0.0113404635, -0.037592266, 0.014819776, -0.005568861, 0.013825687, 0.017134957, 0.001968558, -0.021556038, 0.056453798, 0.0017069556, -0.009404606, 0.009842791, 0.02172608, -0.0067493417, 0.014728215, 0.00878984, -0.018286008, 0.01642863, 0.006304618, -0.023151813, 0.009731609, -0.0062457575, 0.011464725, -0.01866533, -0.009496167, 0.007403348, -0.011020001, 0.0038717154, 0.00972507, -0.031758532, -0.010228653, -0.015996987, 0.008384357, 0.0018998875, 0.020234946, -0.02733745, 0.018652251, 0.011229283, -0.0021189793, -0.014466613, -0.018246768, -0.014074209, -0.0106537575, 0.02817458, 0.013642565, 0.007749971, -0.006690481, 0.0053955493, 0.013851847, 0.009306505, 0.0070044044, -0.0015965921, -0.0022481456, -0.025767837, -0.0016824304, 0.02733745, -0.002951202, 0.018299088, 0.021320596, -0.028593142, 0.0017134957, -0.015552263, 0.036179613, -0.010954601, -0.014453532, -0.005362849, -0.025754755, 0.0014151054, -0.0055982913, -0.013551004, 0.0053922795, -0.009417687, 0.0050947065, 0.0112685235, 0.01861301, 0.025924798, 0.00058533537, -0.017723562, -0.008855241, -0.0075603095, 0.027075848, 0.016598672, -0.0017331159, -0.004211799, 0.01522526, -0.026644204, 0.018848453, 0.0065073594, -0.0028253058, -0.0076060896, 0.012550375, 0.0034008313, -0.030790603, 0.002516288, -0.021268275, 0.0025244632, -0.005313799, -0.0014077479, 0.0015107539, -0.009640048, -0.008253556, -0.023387255, -0.024368264, -0.027049689, -0.00092950603, 0.00087718555, 0.00011823202, 0.017723562, -0.0239497, -0.009947431, -0.00163992, 0.0102024935, -0.031078365, -0.04379224, -0.011870209, 0.0063569383, 0.0218438, 0.015905425, 0.034243755, -0.008430137, 0.03497624, 0.019463219, 0.0010153443, 0.00015430454, -0.0060626357, 0.013799527, -0.06629005, 0.036755137, 0.018756893, 0.011170423, 0.032569498, -0.008443218, 0.026107918, 0.010594897, -0.008933722, -0.014610494, -0.023622697, -0.011641307, 0.0030721931, -0.011229283, -0.00072063284, 0.012393414, -0.014597414, -0.015015977, 0.071522094, 0.017775884, -0.029220987, -0.0095157875, 0.023243373, -0.028985545, -0.0049704458, -0.0065073594, -0.006661051, -0.014963658, 0.020967431, -0.00006550279, -0.0013088295, 0.01975098, 0.009430766, 0.0014952212, 0.01962018, -0.009718529, 0.009267265, 0.039737403, 0.022013841, -0.0054347897, 0.010091312, -0.02718049, -0.023792738, -0.022746328, -0.026487242, -0.03573489, -0.017723562, 0.0049867956, -0.005673502, 0.009659668, -0.016821034, -0.0390834, 0.009509247, 0.014545093, 0.04007749, 0.0084497575, 0.02817458, 0.0099408915, -0.013472524, -0.016114708, 0.028148418, 0.015683064, -0.0009205134, 0.0010096218, 0.01844297, -0.001850837, -0.020823551, -0.01089574, 0.012341093, -0.04996606, -0.014335811, 0.015604583, -0.009156084, -0.00436876, -0.008855241, -0.016860275, 0.0019244127, 0.014100369, -0.02504843, 0.010424855, 0.001983273, -0.030921403, -0.047925558, -0.011942149, -0.014558174, 0.0027811604, 0.010509877, -0.0136164045, 0.0014894987, 0.0009164259, 0.0106995385, 0.014597414, 0.020483468, 0.004143128, -0.0028759914, -0.0067035616, 0.007769591, -0.020470388, -0.00028776264, -0.00763879, 0.0091364635, 0.027023528, 0.00545441, 0.01083688, -0.004649983, 0.01625859, -0.007213686, 0.0111508025, -0.001973463, -0.01090882, -0.015866185, -0.0050391164, 0.011418945, 0.015434542, -0.020692749, -0.013152061, -0.022223124, 0.0023609616, -0.01858685, -0.012641936, 0.019031575, -0.014532013, -0.0054871105, -0.0094438465, 0.005022766, 0.0053824694, 0.017252678, 0.0034923921, -0.0151991, 0.017710483, -0.04245807, 0.008881401, 0.0034825818, 0.0064942795, -0.015748464, -0.0071352054, 0.011301223, -0.0069324635, 0.01408729, -0.038743313, -0.019293176, -0.014702055, -0.009208404, 0.00432952, -0.021477558, -0.010640678, 0.010012832, 0.015094458, 0.009280345, -0.02077123, -0.02494379, 0.012635396, 0.0021238844, -0.011752488, 0.028619302, -0.024773747, 0.007965793, 0.008848701, 0.013969569, 0.006860523, -0.034191433, -0.00011067008, -0.008175075, 0.017815124, 0.0091364635, -0.012962399, -0.019855622, -0.013387503, -0.022367004, 0.021385996, 0.013943408, -0.021542957, 0.010019372, 0.027703693, 0.0058500837, -0.0016202999, 0.0306598, -0.0012123636, 0.006111686, -0.023217212, -0.028540822, -0.014034969, -0.0067820423, 0.030738281, 0.003590493, -0.004908315, -0.032988064, -0.0035316325, -0.041804064, 0.0141134495, -0.022000762, 0.0136164045, 0.030764442, -0.0040057865, 0.0040548374, 0.021242116, 0.010065152, -0.0031310536, -0.029848834, -0.0050587365, -0.0028563712, -0.0018328518, -0.003597033, 0.014793616, -0.019554779, -0.0020143385, 0.029874993, -0.0034302615, -0.006455039, 0.014728215, -0.034479197, -0.009731609, -0.010725698, 0.015591503, 0.0067951223, 0.0044308905, 0.009509247, -0.012301853, 0.0017707213, -0.00058697036, 0.010097853, -0.012386873, -0.0082600955, 0.0023969319, -0.0043033594, 0.028854745, -0.0066839415, -0.006742802, -0.00549365, -0.021137474, 0.017331159, -0.011392784, -0.0044308905, 0.01747504, 0.0050521963, -0.03413911, -0.028828584, 0.0075799297, 0.014832856, -0.014571253, 0.0045191813, -0.0038847956, 0.02172608, -0.008462838, 0.018809212, -0.016611753, 0.009156084, -0.02393662, 0.0037180241, -0.03563025, 0.0262518, 0.010032452, -0.01523834, -0.0015123888, 0.0066774013, -0.0015393667, -0.024054341, -0.025506234, -0.010477176, -0.0048559946, -0.0070305644, -0.010869579, -0.017252678, -0.029953474, 0.0061738165, -0.024420584, -0.04258887, 0.00983625, 0.21576966, -0.004787324, 0.0048592645, 0.012046791, -0.014924417, 0.024669107, 0.036493536, 0.016951835, -0.0010063518, -0.0034596918, -0.009299966, 0.008299336, -0.014832856, 0.0019113325, -0.0090645235, -0.018926933, -0.037435304, -0.0015508117, -0.016768714, -0.018756893, 0.021869961, -0.00439492, -0.003606843, -0.010280974, 0.015983906, -0.0073771877, -0.0032471397, 0.0007529244, 0.0005877879, -0.0003386525, -0.006314428, -0.031026045, -0.0051045166, -0.016533272, -0.013577164, -0.004048297, 0.0063536684, 0.005035846, -0.0060887956, 0.015643824, 0.02494379, 0.015042138, -0.015787704, 0.002517923, -0.015316821, 0.0149898175, -0.008364737, -0.01752736, 0.010254814, 0.006435419, -0.024185142, -0.01083688, 0.016624833, 0.051718794, -0.0037572645, -0.00067485246, 0.010575277, 0.022353925, 0.007429508, 0.004564962, -0.005689852, 0.019044654, 0.006186897, 0.019842543, -0.024381343, 0.018992335, -0.048788846, 0.005163377, 0.023034092, -0.021699918, -0.006723182, -0.036022652, -0.012138351, -0.01204025, -0.0103921555, -0.0348716, 0.018024405, 0.008959882, 0.013158601, -0.007043645, 0.00870482, 0.0023282613, 0.008024653, 0.004247769, -0.017252678, -0.020470388, 0.033406626, 0.0104640955, -0.011660927, 0.0021663948, 0.0018230417, -0.02943027, 0.0013913978, -0.0047480837, 0.013184761, 0.032386377, -0.0060855257, -0.001146963, -0.006978244, -0.0086852, -0.018338328, -0.036205772, 0.0196071, -0.01417885, -0.021608358, 0.0071679056, -0.0008632879, 0.012256073, 0.039449643, -0.030712122, -0.0015181114, -0.0136164045, 0.0074360482, -0.010326754, 0.022523966, 0.015395301, -0.011713248, -0.02067967, 0.0015311915, -0.018979253, -0.007318327, -0.038560193, -0.01304088, -0.008914102, -0.010692998, -0.028017618, 0.003175199, 0.020143384, 0.04052221, -0.014034969, 0.028200738, -0.03612729, -0.012936238, -0.0063536684, 0.008358196, 0.00763225, 0.005778143, -0.006886683, 0.010241734, 0.011347004, 0.021215955, 0.01746196, 0.00326022, -0.010287514, 0.016873354, -0.011490885, 0.012144892, 0.0038488253, -0.0055230805, 0.001145328, -0.015552263, -0.024747588, 0.004551882, -0.0039796266, 0.03709522, -0.021032833, -0.020418067, -0.016127788, -0.0037899648, 0.00014061129, -0.016716393, 0.013027799, 0.0050489265, -0.018142127, -0.011732868, 0.01852145, -0.16721626, 0.024682187, 0.008887942, 0.002089549, 0.041908704, -0.002836751, 0.01644171, -0.009646588, -0.008044274, 0.002403472, 0.004103888, -0.00068098377, -0.0370429, -0.0032520448, -0.005133947, 0.007063265, -0.010712618, 0.008606719, 0.019437058, 0.0027811604, 0.0071352054, -0.0064910096, 0.0055525107, 0.007763051, 0.011046161, 0.012968939, 0.009384986, -0.016624833, -0.010228653, -0.014963658, -0.009463467, -0.0031375939, 0.011674007, 0.0144927725, 0.024224382, 0.0009205134, -0.013269782, -0.017605841, -0.0122887725, 0.012779277, 0.005261478, 0.014244251, 0.013263241, -0.004829834, 0.014702055, 0.04805636, 0.003606843, 0.0020143385, -0.0008780031, -0.012347633, 0.006304618, -0.029744193, 0.008135835, 0.00035193696, 0.023871219, 0.022353925, 0.010006292, -0.012831598, 0.014754375, -0.03272646, -0.009201865, 0.0013251797, 0.009522327, -0.014675895, -0.025571635, -0.015879266, -0.004022137, 0.0018819022, -0.02956107, 0.019737901, -0.036781296, -0.013348263, 0.02623872, -0.017893605, 0.006177087, -0.0050718165, -0.014715135, 0.006958624, -0.010071692, -0.0097054485, -0.038455553, 0.03361591, 0.003809585, -0.008436677, 0.014061129, 0.0005665327, -0.0033109053, -0.014675895, -0.013721046, -0.030947564, -0.0021124394, -0.016088547, -0.011922529, -0.03152309, 0.020575028, 0.029325629, -0.0003155579, 0.0038913356, 0.012491515, -0.011883289, -0.009404606, 0.018037485, -0.021477558, 0.020431148, 0.030764442, 0.02510075, 0.021987682, 0.0077761314, 0.042981274, -0.0045322618, -0.0073902677, 0.008574018, -0.002056849, 0.027625212, -0.0017592761, 0.019214695, 0.010019372, -0.020012584, 0.04146398, -0.019201616, 0.03897876, 0.0049671754, -0.02935179, 0.021072073, -0.021490637, -0.010287514, -0.106419854, -0.0026716145, -0.019018494, 0.013171681, -0.0020094335, 0.010640678, -0.0046401727, 0.012321473, -0.0073771877, 0.008390897, -0.0058533535, -0.0218438, 0.019123135, -0.013930327, 0.035342485, 0.000036966667, -0.015460702, 0.0045682318, -0.023191053, 0.015813865, -0.010542576, 0.005549241, 0.0137341255, -0.013407123, 0.012962399, -0.0041202377, -0.032255575, 0.0044374308, 0.007534149, 0.00766495, -0.01194869, -0.015604583, 0.01836449, -0.020182624, 0.0030934485, -0.001960383, -0.030110436, 0.0072725466, 0.034400716, -0.033668227, 0.009175704, 0.009620428, 0.0027942406, -0.017932845, 0.006458309, 0.0027746204, -0.008384357, 0.012419574, -0.0028988817, -0.027546732, -0.030738281, 0.00016186648, -0.029848834, 0.007939633, 0.017344238, 0.0048690746, 0.0046532527, 0.008927182, 0.028017618, 0.005791223, -0.0019947183, -0.01314552, 0.002277576, 0.00044431532, -0.0069847843, -0.0079527125, -0.014976737, 0.011732868, 0.00035398075, -0.0018017865, 0.010137093, 0.021359837, -0.008220855, 0.008632879, -0.037775386, 0.020195706, -0.013995728, 0.0012401589, 0.013799527, -0.017815124, -0.007920013, -0.021346755, 0.009993211, -0.01633707, 0.006873603, 0.004810214, 0.0032896502, -0.00025383607, -0.010163253, -0.012275693, 0.0037605346, -0.008999122, -0.011817888, -0.03623193, -0.021242116, 0.015826944, 0.0061607365, -0.0064975494, -0.0067951223, -0.012393414, -0.013551004, 0.008626339, -0.06644701, 0.024656026, 0.0024590625, -0.00020263967, -0.002517923, -0.012798897, 0.016925676, -0.0026814246, -0.004296819, -0.005748713, -0.0056931223, 0.02725897, 0.0030967183, -0.002411647, -0.006736262, -0.019463219, -0.00075496815, -0.008488998, 0.016572513, 0.011360084, 0.009489627, 0.012504594, 0.001317822, 0.007926553, -0.0066185407, -0.0020862792, -0.0036885939, 0.021569118, -0.0022138103, 0.0018606471, -0.009646588, -0.023491895, -0.010732238, 0.0017592761, 0.0011714882, 0.008273176, -0.001744561, 0.0064942795, 0.01635015, 0.06372634, -0.02731129, -0.014571253, 0.010294055, -0.011052702, -0.019267017, 0.0031441338, 0.008593638, 0.007893852, 0.03495008, -0.019175455, 0.016703313, 0.037644584, -0.00975123, 0.00041938134, -0.00976431, -0.0039730864, 0.018181367, -0.00050440215, 0.014767455, -0.0042379587, 0.02524463, -0.0049966057, 0.009280345, 0.014924417, -0.0012115461, -0.029874993, -0.0132370815, -0.010006292, 0.0035152822, -0.016206268, -0.004914855, -0.023675017, -0.0016619927, 0.005330149, -0.004453781, 0.0003075872, -0.0053857393, -0.0017069556, -0.008384357, 0.021516798, -0.006232677, 0.0031097985, -0.042039506, 0.00438184, 0.024917629, 0.001138788, 0.006853983, 0.015696144, -0.013348263, 0.016114708, -0.010791099, 0.017867444, -0.0035185523, 0.022445485, -0.014270411, 0.014963658, -0.040705334, -0.007279087, 0.01627167, 0.012373794, 0.0070567247, 0.0008632879, 0.0070567247, -0.011588986, -0.011772108, 0.0046140123, -0.032229416, -0.043059755, -0.011013461, 0.004780784, 0.023635777, -0.016546352, -0.013034339, 0.011445105, -0.005814113, 0.00977085, -0.0045061014, -0.002202365, -0.017200358, 0.014675895, 0.007501449, 0.00761917, 0.014021888, 0.0014012079, 0.002275941, 0.015630743, 0.010405235, -0.004264119, -0.009424226, -0.02506151, -0.0010733873, 0.0074949088, -0.01963326, 0.00031433164, -0.018063646, -0.017383479, 0.0000909988, 0.018050566, -0.027102008, 0.028645463, -0.008332036, 0.0029528372, 0.014702055, -0.027154328, 0.021909202, 0.0034466116, 0.03897876, -0.0031228787, -0.015015977, 0.0062915375, -0.035604086, 0.02185688, 0.008613259, -0.008194695, 0.030188916, -0.0010096218, 0.0070174844, 0.0066970214, 0.004607472, 0.022000762, 0.012655016, 0.0087179, -0.007279087, -0.016860275, 0.000018994277, 0.0010177968, 0.01618011, -0.012046791, -0.035473283, 0.013361342, -0.0067035616, -0.013577164, 0.012825058, -0.007965793, -0.021699918, 0.011588986, -0.007161366, -0.0050652763, -0.00540863, -0.019123135, 0.026304122, -0.0042150686, -0.022393165, -0.013943408, 0.00078439846, -0.005801033, -0.00087555056, -0.027232809 }; + + var vectorOptions = new VectorSearchOptions { IndexName = "vector_search_embedded_movies" }; var vectorPipeline = new EmptyPipelineDefinition().VectorSearch(m => m.Embedding, vector, limit, vectorOptions); var searchDefinition = Builders.Search.Text(new[]{"fullplot", "title"}, "ape"); - var searchPipeline = new EmptyPipelineDefinition().Search(searchDefinition, indexName: "search_embedded_movies").Limit(limit); + var searchPipeline = new EmptyPipelineDefinition().Search(searchDefinition, indexName: "sample_mflix__embedded_movies").Limit(limit); - var result = GetTestCollection() + var result = GetEmbeddedMoviesCollection() .Aggregate() - .RankFusion(new Dictionary>() + .RankFusion(new Dictionary> { { "vector", vectorPipeline }, { "search", searchPipeline } - }); - - result.Stages.Count.Should().Be(1); + }) + .Limit(limit) + .ToList(); - var serializerRegistry = BsonSerializer.SerializerRegistry; - var inputSerializer = serializerRegistry.GetSerializer(); - var renderedStage = result.Stages[0].Render(inputSerializer, serializerRegistry); - renderedStage.Document.Should().Be( - """ - { - $rankFusion: { - input: { - pipelines: { - vector: [{ - $vectorSearch: { - queryVector: [1.0, 2.0, 3.0], - path: "plot_embedding", - limit: 5, - numCandidates: 50, - index: "vector_search_embedded_movies" - } - }], - search: [{ - $search: { - text: {query: "ape", path: ["fullplot", "title"]}, - index: "search_embedded_movies" - } - }, - { $limit: 5 } - ] - } - }} - } - """); + result.Count.Should().Be(limit); + result.Select(m => m.Title).Should().BeEquivalentTo("Tarzan the Ape Man", "King Kong", + "Battle for the Planet of the Apes", "King Kong Lives", "Mighty Joe Young"); } [Fact] @@ -568,7 +540,7 @@ public void SearchSequenceToken() .MetaSearchSequenceToken(x => x.PaginationToken); // Base search - var baseSearchResults = GetSynonymTestCollection() + var baseSearchResults = GetMoviesCollection() .Aggregate() .Search(searchDefinition, searchOptions) .Project(projection) @@ -585,7 +557,7 @@ public void SearchSequenceToken() // Testing SearchAfter // We're searching after the 2nd result of the base search searchOptions.SearchAfter = baseSearchResults[1].PaginationToken; - var searchAfterResults = GetSynonymTestCollection() + var searchAfterResults = GetMoviesCollection() .Aggregate() .Search(searchDefinition, searchOptions) .Project(projection) @@ -601,7 +573,7 @@ public void SearchSequenceToken() // We're searching before the 4th result of the base search searchOptions.SearchAfter = null; searchOptions.SearchBefore = baseSearchResults[3].PaginationToken; - var searchBeforeResults = GetSynonymTestCollection() + var searchBeforeResults = GetMoviesCollection() .Aggregate() .Search(searchDefinition, searchOptions) .Project(projection) @@ -700,7 +672,7 @@ public void Sort() [Fact] public void Sort_MetaSearchScore() { - var results = GetSynonymTestCollection().Aggregate() + var results = GetMoviesCollection().Aggregate() .Search( Builders.Search.QueryString(x => x.Title, "dance"), new() { Sort = Builders.Sort.MetaSearchScoreAscending() }) @@ -747,7 +719,7 @@ public void Text() public void TextMatchCriteria() { var result = - GetSynonymTestCollection().Aggregate() + GetMoviesCollection().Aggregate() .Search( Builders.Search.Text("plot", "attire", new SearchTextOptions { Synonyms = "attireSynonyms", MatchCriteria = MatchCriteria.Any}), indexName: "synonyms-tests") @@ -770,7 +742,7 @@ public void Synonyms(string query, string synonym, string expected) { var sortDefinition = Builders.Sort.Ascending(x => x.Title); var result = - GetSynonymTestCollection().Aggregate() + GetMoviesCollection().Aggregate() .Search(Builders.Search.Text(x => x.Title, query, synonym), indexName: "synonyms-tests") .Sort(sortDefinition) .Project(Builders.Projection.Include("Title").Exclude("_id")) @@ -835,7 +807,7 @@ private HistoricalDocument SearchSingle( } private List SearchMultipleSynonymMapping(params SearchDefinition[] clauses) => - GetSynonymTestCollection().Aggregate() + GetMoviesCollection().Aggregate() .Search(Builders.Search.Compound().Should(clauses), indexName: "synonyms-tests") .Project(Builders.Projection.Include("Title").Exclude("_id")) .ToList(); @@ -848,9 +820,13 @@ private IMongoCollection GetTestCollection() => _mongoClient .GetDatabase("sample_training") .GetCollection("posts"); - private IMongoCollection GetSynonymTestCollection() => _mongoClient + private IMongoCollection GetEmbeddedMoviesCollection() => _mongoClient .GetDatabase("sample_mflix") - .GetCollection("movies"); + .GetCollection("embedded_movies"); + + private IMongoCollection GetMoviesCollection() => _mongoClient + .GetDatabase("sample_mflix") + .GetCollection("movies"); private IMongoCollection GetGeoTestCollection() => _mongoClient .GetDatabase("sample_airbnb") @@ -886,6 +862,7 @@ public class Movie public string PaginationToken { get; set; } } + [BsonIgnoreExtraElements] public class EmbeddedMovie : Movie { [BsonElement("plot_embedding")] From 75200dd93b80fd332eb53d6f1f88a51b77c58b47 Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Mon, 26 May 2025 10:49:29 -0700 Subject: [PATCH 08/24] CSHARP-5596: Remove unused *OpcodeOperation classes (#1698) --- .../Core/Operations/DeleteOpcodeOperation.cs | 121 ----------- .../DeleteOpcodeOperationEmulator.cs | 144 ------------- .../Core/Operations/InsertOpcodeOperation.cs | 161 -------------- .../InsertOpcodeOperationEmulator.cs | 203 ------------------ .../Core/Operations/UpdateOpcodeOperation.cs | 133 ------------ .../UpdateOpcodeOperationEmulator.cs | 168 --------------- .../Operations/InsertOpcodeOperationTests.cs | 176 --------------- .../Operations/UpdateOpcodeOperationTests.cs | 64 ------ 8 files changed, 1170 deletions(-) delete mode 100644 src/MongoDB.Driver/Core/Operations/DeleteOpcodeOperation.cs delete mode 100644 src/MongoDB.Driver/Core/Operations/DeleteOpcodeOperationEmulator.cs delete mode 100644 src/MongoDB.Driver/Core/Operations/InsertOpcodeOperation.cs delete mode 100644 src/MongoDB.Driver/Core/Operations/InsertOpcodeOperationEmulator.cs delete mode 100644 src/MongoDB.Driver/Core/Operations/UpdateOpcodeOperation.cs delete mode 100644 src/MongoDB.Driver/Core/Operations/UpdateOpcodeOperationEmulator.cs delete mode 100644 tests/MongoDB.Driver.Tests/Core/Operations/InsertOpcodeOperationTests.cs delete mode 100644 tests/MongoDB.Driver.Tests/Core/Operations/UpdateOpcodeOperationTests.cs diff --git a/src/MongoDB.Driver/Core/Operations/DeleteOpcodeOperation.cs b/src/MongoDB.Driver/Core/Operations/DeleteOpcodeOperation.cs deleted file mode 100644 index d21c46885b9..00000000000 --- a/src/MongoDB.Driver/Core/Operations/DeleteOpcodeOperation.cs +++ /dev/null @@ -1,121 +0,0 @@ -/* Copyright 2013-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System.Threading; -using System.Threading.Tasks; -using MongoDB.Driver.Core.Bindings; -using MongoDB.Driver.Core.Events; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; - -namespace MongoDB.Driver.Core.Operations -{ - internal sealed class DeleteOpcodeOperation : IWriteOperation, IExecutableInRetryableWriteContext - { - private readonly CollectionNamespace _collectionNamespace; - private readonly MessageEncoderSettings _messageEncoderSettings; - private readonly DeleteRequest _request; - private bool _retryRequested; - private WriteConcern _writeConcern = WriteConcern.Acknowledged; - - public DeleteOpcodeOperation( - CollectionNamespace collectionNamespace, - DeleteRequest request, - MessageEncoderSettings messageEncoderSettings) - { - _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); - _request = Ensure.IsNotNull(request, nameof(request)); - _messageEncoderSettings = messageEncoderSettings; - } - - public CollectionNamespace CollectionNamespace - { - get { return _collectionNamespace; } - } - - public DeleteRequest Request - { - get { return _request; } - } - - public MessageEncoderSettings MessageEncoderSettings - { - get { return _messageEncoderSettings; } - } - - public bool RetryRequested - { - get { return _retryRequested; } - set { _retryRequested = value; } - } - - public WriteConcern WriteConcern - { - get { return _writeConcern; } - set { _writeConcern = Ensure.IsNotNull(value, nameof(value)); } - } - - public WriteConcernResult Execute(IWriteBinding binding, CancellationToken cancellationToken) - { - Ensure.IsNotNull(binding, nameof(binding)); - - using (var context = RetryableWriteContext.Create(binding, false, cancellationToken)) - { - return Execute(context, cancellationToken); - } - } - - public WriteConcernResult Execute(RetryableWriteContext context, CancellationToken cancellationToken) - { - Ensure.IsNotNull(context, nameof(context)); - - using (EventContext.BeginOperation()) - { - var emulator = CreateEmulator(); - return emulator.Execute(context, cancellationToken); - } - } - - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) - { - Ensure.IsNotNull(binding, nameof(binding)); - - using (var context = await RetryableWriteContext.CreateAsync(binding, false, cancellationToken).ConfigureAwait(false)) - { - return await ExecuteAsync(context, cancellationToken).ConfigureAwait(false); - } - } - - public async Task ExecuteAsync(RetryableWriteContext context, CancellationToken cancellationToken) - { - Ensure.IsNotNull(context, nameof(context)); - - using (EventContext.BeginOperation()) - { - var emulator = CreateEmulator(); - return await emulator.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); - } - } - - private IExecutableInRetryableWriteContext CreateEmulator() - { - return new DeleteOpcodeOperationEmulator(_collectionNamespace, _request, _messageEncoderSettings) - { - RetryRequested = _retryRequested, - WriteConcern = _writeConcern - }; - } - } -} diff --git a/src/MongoDB.Driver/Core/Operations/DeleteOpcodeOperationEmulator.cs b/src/MongoDB.Driver/Core/Operations/DeleteOpcodeOperationEmulator.cs deleted file mode 100644 index e325eee4b95..00000000000 --- a/src/MongoDB.Driver/Core/Operations/DeleteOpcodeOperationEmulator.cs +++ /dev/null @@ -1,144 +0,0 @@ -/* Copyright 2013-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System.Threading; -using System.Threading.Tasks; -using MongoDB.Driver.Core.Bindings; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; - -namespace MongoDB.Driver.Core.Operations -{ - internal class DeleteOpcodeOperationEmulator : IExecutableInRetryableWriteContext - { - // fields - private readonly CollectionNamespace _collectionNamespace; - private readonly DeleteRequest _request; - private readonly MessageEncoderSettings _messageEncoderSettings; - private bool _retryRequested; - private WriteConcern _writeConcern = WriteConcern.Acknowledged; - - // constructors - public DeleteOpcodeOperationEmulator( - CollectionNamespace collectionNamespace, - DeleteRequest request, - MessageEncoderSettings messageEncoderSettings) - { - _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); - _request = Ensure.IsNotNull(request, nameof(request)); - _messageEncoderSettings = messageEncoderSettings; - } - - // properties - public CollectionNamespace CollectionNamespace - { - get { return _collectionNamespace; } - } - - public DeleteRequest Request - { - get { return _request; } - } - - public MessageEncoderSettings MessageEncoderSettings - { - get { return _messageEncoderSettings; } - } - - public bool RetryRequested - { - get { return _retryRequested; } - set { _retryRequested = value; } - } - - public WriteConcern WriteConcern - { - get { return _writeConcern; } - set { _writeConcern = Ensure.IsNotNull(value, nameof(value)); } - } - - // public methods - public WriteConcernResult Execute(RetryableWriteContext context, CancellationToken cancellationToken) - { - Ensure.IsNotNull(context, nameof(context)); - - var operation = CreateOperation(); - BulkWriteOperationResult result; - MongoBulkWriteOperationException exception = null; - try - { - result = operation.Execute(context, cancellationToken); - } - catch (MongoBulkWriteOperationException ex) - { - result = ex.Result; - exception = ex; - } - - return CreateResultOrThrow(context.Channel, result, exception); - } - - public async Task ExecuteAsync(RetryableWriteContext context, CancellationToken cancellationToken) - { - Ensure.IsNotNull(context, nameof(context)); - - var operation = CreateOperation(); - BulkWriteOperationResult result; - MongoBulkWriteOperationException exception = null; - try - { - result = await operation.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); - } - catch (MongoBulkWriteOperationException ex) - { - result = ex.Result; - exception = ex; - } - - return CreateResultOrThrow(context.Channel, result, exception); - } - - // private methods - private BulkDeleteOperation CreateOperation() - { - var requests = new[] { _request }; - return new BulkDeleteOperation(_collectionNamespace, requests, _messageEncoderSettings) - { - RetryRequested = _retryRequested, - WriteConcern = _writeConcern - }; - } - - private WriteConcernResult CreateResultOrThrow(IChannelHandle channel, BulkWriteOperationResult result, MongoBulkWriteOperationException exception) - { - var converter = new BulkWriteOperationResultConverter(); - if (exception != null) - { - throw converter.ToWriteConcernException(channel.ConnectionDescription.ConnectionId, exception); - } - else - { - if (_writeConcern.IsAcknowledged) - { - return converter.ToWriteConcernResult(result); - } - else - { - return null; - } - } - } - } -} diff --git a/src/MongoDB.Driver/Core/Operations/InsertOpcodeOperation.cs b/src/MongoDB.Driver/Core/Operations/InsertOpcodeOperation.cs deleted file mode 100644 index 71316bccbff..00000000000 --- a/src/MongoDB.Driver/Core/Operations/InsertOpcodeOperation.cs +++ /dev/null @@ -1,161 +0,0 @@ -/* Copyright 2013-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MongoDB.Bson.Serialization; -using MongoDB.Driver.Core.Bindings; -using MongoDB.Driver.Core.Events; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; - -namespace MongoDB.Driver.Core.Operations -{ - internal sealed class InsertOpcodeOperation : IWriteOperation> - { - private bool? _bypassDocumentValidation; - private readonly CollectionNamespace _collectionNamespace; - private bool _continueOnError; - private readonly IReadOnlyList _documents; - private readonly BatchableSource _documentSource; - private int? _maxBatchCount; - private int? _maxDocumentSize; - private int? _maxMessageSize; - private readonly MessageEncoderSettings _messageEncoderSettings; - private bool _retryRequested; - private readonly IBsonSerializer _serializer; - private WriteConcern _writeConcern; - - public InsertOpcodeOperation(CollectionNamespace collectionNamespace, IEnumerable documents, IBsonSerializer serializer, MessageEncoderSettings messageEncoderSettings) - { - _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); - _documents = Ensure.IsNotNull(documents, nameof(documents)).ToList(); - _serializer = Ensure.IsNotNull(serializer, nameof(serializer)); - _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); - _writeConcern = WriteConcern.Acknowledged; - - _documentSource = new BatchableSource(_documents, canBeSplit: true); - } - - public bool? BypassDocumentValidation - { - get { return _bypassDocumentValidation; } - set { _bypassDocumentValidation = value; } - } - - public CollectionNamespace CollectionNamespace - { - get { return _collectionNamespace; } - } - - public bool ContinueOnError - { - get { return _continueOnError; } - set { _continueOnError = value; } - } - - public IReadOnlyList Documents - { - get { return _documents; } - } - - public BatchableSource DocumentSource - { - get { return _documentSource; } - } - - public int? MaxBatchCount - { - get { return _maxBatchCount; } - set { _maxBatchCount = Ensure.IsNullOrGreaterThanZero(value, nameof(value)); } - } - - public int? MaxDocumentSize - { - get { return _maxDocumentSize; } - set { _maxDocumentSize = Ensure.IsNullOrGreaterThanZero(value, nameof(value)); } - } - - public int? MaxMessageSize - { - get { return _maxMessageSize; } - set { _maxMessageSize = Ensure.IsNullOrGreaterThanZero(value, nameof(value)); } - } - - public MessageEncoderSettings MessageEncoderSettings - { - get { return _messageEncoderSettings; } - } - - public bool RetryRequested - { - get { return _retryRequested; } - set { _retryRequested = value; } - } - - public IBsonSerializer Serializer - { - get { return _serializer; } - } - - public WriteConcern WriteConcern - { - get { return _writeConcern; } - set { _writeConcern = Ensure.IsNotNull(value, nameof(value)); } - } - - public IEnumerable Execute(IWriteBinding binding, CancellationToken cancellationToken) - { - Ensure.IsNotNull(binding, nameof(binding)); - - using (EventContext.BeginOperation()) - using (var context = RetryableWriteContext.Create(binding, false, cancellationToken)) - { - var emulator = CreateEmulator(); - var result = emulator.Execute(context, cancellationToken); - return result != null ? new[] { result } : null; - } - } - - public async Task> ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) - { - Ensure.IsNotNull(binding, nameof(binding)); - - using (EventContext.BeginOperation()) - using (var context = await RetryableWriteContext.CreateAsync(binding, false, cancellationToken).ConfigureAwait(false)) - { - var emulator = CreateEmulator(); - var result = await emulator.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); - return result != null ? new[] { result } : null; - } - } - - private InsertOpcodeOperationEmulator CreateEmulator() - { - return new InsertOpcodeOperationEmulator(_collectionNamespace, _serializer, _documentSource, _messageEncoderSettings) - { - BypassDocumentValidation = _bypassDocumentValidation, - ContinueOnError = _continueOnError, - MaxBatchCount = _maxBatchCount, - MaxDocumentSize = _maxDocumentSize, - MaxMessageSize = _maxMessageSize, - RetryRequested = _retryRequested, - WriteConcern = _writeConcern - }; - } - } -} diff --git a/src/MongoDB.Driver/Core/Operations/InsertOpcodeOperationEmulator.cs b/src/MongoDB.Driver/Core/Operations/InsertOpcodeOperationEmulator.cs deleted file mode 100644 index 7b22092b54e..00000000000 --- a/src/MongoDB.Driver/Core/Operations/InsertOpcodeOperationEmulator.cs +++ /dev/null @@ -1,203 +0,0 @@ -/* Copyright 2013-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MongoDB.Bson; -using MongoDB.Bson.Serialization; -using MongoDB.Driver.Core.Bindings; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; - -namespace MongoDB.Driver.Core.Operations -{ - internal class InsertOpcodeOperationEmulator : IExecutableInRetryableWriteContext - { - // fields - private bool? _bypassDocumentValidation; - private readonly CollectionNamespace _collectionNamespace; - private bool _continueOnError; - private readonly BatchableSource _documentSource; - private int? _maxBatchCount; - private int? _maxDocumentSize; - private int? _maxMessageSize; - private readonly MessageEncoderSettings _messageEncoderSettings; - private bool _retryRequested; - private readonly IBsonSerializer _serializer; - private WriteConcern _writeConcern = WriteConcern.Acknowledged; - - // constructors - public InsertOpcodeOperationEmulator( - CollectionNamespace collectionNamespace, - IBsonSerializer serializer, - BatchableSource documentSource, - MessageEncoderSettings messageEncoderSettings) - { - _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); - _serializer = Ensure.IsNotNull(serializer, nameof(serializer)); - _documentSource = Ensure.IsNotNull(documentSource, nameof(documentSource)); - _messageEncoderSettings = messageEncoderSettings; - - if (documentSource.Items.Skip(documentSource.Offset).Take(documentSource.Count).Any(d => d == null)) - { - throw new ArgumentException("Batch contains one or more null documents."); - } - } - - // properties - public bool? BypassDocumentValidation - { - get { return _bypassDocumentValidation; } - set { _bypassDocumentValidation = value; } - } - - public CollectionNamespace CollectionNamespace - { - get { return _collectionNamespace; } - } - - public bool ContinueOnError - { - get { return _continueOnError; } - set { _continueOnError = value; } - } - - public BatchableSource DocumentSource - { - get { return _documentSource; } - } - - public int? MaxBatchCount - { - get { return _maxBatchCount; } - set { _maxBatchCount = Ensure.IsNullOrGreaterThanZero(value, nameof(value)); } - } - - public int? MaxDocumentSize - { - get { return _maxDocumentSize; } - set { _maxDocumentSize = Ensure.IsNullOrGreaterThanZero(value, nameof(value)); } - } - - public int? MaxMessageSize - { - get { return _maxMessageSize; } - set { _maxMessageSize = Ensure.IsNullOrGreaterThanZero(value, nameof(value)); } - } - - public MessageEncoderSettings MessageEncoderSettings - { - get { return _messageEncoderSettings; } - } - - public bool RetryRequested - { - get { return _retryRequested; } - set { _retryRequested = value; } - } - - public IBsonSerializer Serializer - { - get { return _serializer; } - } - - public WriteConcern WriteConcern - { - get { return _writeConcern; } - set { _writeConcern = Ensure.IsNotNull(value, nameof(value)); } - } - - // public methods - public WriteConcernResult Execute(RetryableWriteContext context, CancellationToken cancellationToken) - { - Ensure.IsNotNull(context, nameof(context)); - - var operation = CreateOperation(); - BulkWriteOperationResult result; - MongoBulkWriteOperationException exception = null; - try - { - result = operation.Execute(context, cancellationToken); - } - catch (MongoBulkWriteOperationException ex) - { - result = ex.Result; - exception = ex; - } - - return CreateResultOrThrow(context.Channel, result, exception); - } - - public async Task ExecuteAsync(RetryableWriteContext context, CancellationToken cancellationToken) - { - Ensure.IsNotNull(context, nameof(context)); - - var operation = CreateOperation(); - BulkWriteOperationResult result; - MongoBulkWriteOperationException exception = null; - try - { - result = await operation.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); - } - catch (MongoBulkWriteOperationException ex) - { - result = ex.Result; - exception = ex; - } - - return CreateResultOrThrow(context.Channel, result, exception); - } - - // private methods - private BulkInsertOperation CreateOperation() - { - var requests = _documentSource.GetBatchItems().Select(d => new InsertRequest(new BsonDocumentWrapper(d, _serializer))); - - return new BulkInsertOperation(_collectionNamespace, requests, _messageEncoderSettings) - { - BypassDocumentValidation = _bypassDocumentValidation, - IsOrdered = !_continueOnError, - MaxBatchCount = _maxBatchCount, - MaxBatchLength = _maxMessageSize, - // ReaderSettings = ? - RetryRequested = _retryRequested, - WriteConcern = _writeConcern, - // WriteSettings = ? - }; - } - - private WriteConcernResult CreateResultOrThrow(IChannel channel, BulkWriteOperationResult result, MongoBulkWriteOperationException exception) - { - var converter = new BulkWriteOperationResultConverter(); - if (exception != null) - { - throw converter.ToWriteConcernException(channel.ConnectionDescription.ConnectionId, exception); - } - else - { - if (_writeConcern.IsAcknowledged) - { - return converter.ToWriteConcernResult(result); - } - else - { - return null; - } - } - } - } -} diff --git a/src/MongoDB.Driver/Core/Operations/UpdateOpcodeOperation.cs b/src/MongoDB.Driver/Core/Operations/UpdateOpcodeOperation.cs deleted file mode 100644 index 72862d1079f..00000000000 --- a/src/MongoDB.Driver/Core/Operations/UpdateOpcodeOperation.cs +++ /dev/null @@ -1,133 +0,0 @@ -/* Copyright 2010-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System.Threading; -using System.Threading.Tasks; -using MongoDB.Driver.Core.Bindings; -using MongoDB.Driver.Core.Events; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; - -namespace MongoDB.Driver.Core.Operations -{ - internal sealed class UpdateOpcodeOperation : IWriteOperation, IExecutableInRetryableWriteContext - { - private bool? _bypassDocumentValidation; - private readonly CollectionNamespace _collectionNamespace; - private int? _maxDocumentSize; - private readonly MessageEncoderSettings _messageEncoderSettings; - private readonly UpdateRequest _request; - private bool _retryRequested; - private WriteConcern _writeConcern = WriteConcern.Acknowledged; - - public UpdateOpcodeOperation( - CollectionNamespace collectionNamespace, - UpdateRequest request, - MessageEncoderSettings messageEncoderSettings) - { - _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); - _request = Ensure.IsNotNull(request, nameof(request)); - _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); - } - - public bool? BypassDocumentValidation - { - get { return _bypassDocumentValidation; } - set { _bypassDocumentValidation = value; } - } - - public CollectionNamespace CollectionNamespace - { - get { return _collectionNamespace; } - } - - public int? MaxDocumentSize - { - get { return _maxDocumentSize; } - set { _maxDocumentSize = Ensure.IsNullOrGreaterThanZero(value, nameof(value)); } - } - - public MessageEncoderSettings MessageEncoderSettings - { - get { return _messageEncoderSettings; } - } - - public UpdateRequest Request - { - get { return _request; } - } - - public bool RetryRequested - { - get { return _retryRequested; } - set { _retryRequested = value; } - } - - public WriteConcern WriteConcern - { - get { return _writeConcern; } - set { _writeConcern = Ensure.IsNotNull(value, nameof(value)); } - } - - public WriteConcernResult Execute(IWriteBinding binding, CancellationToken cancellationToken) - { - Ensure.IsNotNull(binding, nameof(binding)); - - using (var context = RetryableWriteContext.Create(binding, false, cancellationToken)) - { - return Execute(context, cancellationToken); - } - } - - public WriteConcernResult Execute(RetryableWriteContext context, CancellationToken cancellationToken) - { - using (EventContext.BeginOperation()) - { - var emulator = CreateEmulator(); - return emulator.Execute(context, cancellationToken); - } - } - - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) - { - Ensure.IsNotNull(binding, nameof(binding)); - - using (var context = await RetryableWriteContext.CreateAsync(binding, false, cancellationToken).ConfigureAwait(false)) - { - return await ExecuteAsync(context, cancellationToken).ConfigureAwait(false); - } - } - - public async Task ExecuteAsync(RetryableWriteContext context, CancellationToken cancellationToken) - { - using (EventContext.BeginOperation()) - { - var emulator = CreateEmulator(); - return await emulator.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); - } - } - - private UpdateOpcodeOperationEmulator CreateEmulator() - { - return new UpdateOpcodeOperationEmulator(_collectionNamespace, _request, _messageEncoderSettings) - { - BypassDocumentValidation = _bypassDocumentValidation, - MaxDocumentSize = _maxDocumentSize, - RetryRequested = _retryRequested, - WriteConcern = _writeConcern - }; - } - } -} diff --git a/src/MongoDB.Driver/Core/Operations/UpdateOpcodeOperationEmulator.cs b/src/MongoDB.Driver/Core/Operations/UpdateOpcodeOperationEmulator.cs deleted file mode 100644 index 77947fdf372..00000000000 --- a/src/MongoDB.Driver/Core/Operations/UpdateOpcodeOperationEmulator.cs +++ /dev/null @@ -1,168 +0,0 @@ -/* Copyright 2013-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Threading; -using System.Threading.Tasks; -using MongoDB.Driver.Core.Bindings; -using MongoDB.Driver.Core.Connections; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; - -namespace MongoDB.Driver.Core.Operations -{ - internal class UpdateOpcodeOperationEmulator : IExecutableInRetryableWriteContext - { - // fields - private bool? _bypassDocumentValidation; - private readonly CollectionNamespace _collectionNamespace; - private int? _maxDocumentSize; - private readonly MessageEncoderSettings _messageEncoderSettings; - private readonly UpdateRequest _request; - private bool _retryRequested; - private WriteConcern _writeConcern = WriteConcern.Acknowledged; - - // constructors - public UpdateOpcodeOperationEmulator( - CollectionNamespace collectionNamespace, - UpdateRequest request, - MessageEncoderSettings messageEncoderSettings) - { - _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); - _request = Ensure.IsNotNull(request, nameof(request)); - _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); - } - - // properties - public bool? BypassDocumentValidation - { - get { return _bypassDocumentValidation; } - set { _bypassDocumentValidation = value; } - } - - public CollectionNamespace CollectionNamespace - { - get { return _collectionNamespace; } - } - - /// - /// Gets or sets the maximum size of a document. - /// - /// - /// The maximum size of a document. - /// - public int? MaxDocumentSize - { - get { return _maxDocumentSize; } - set { _maxDocumentSize = Ensure.IsNullOrGreaterThanZero(value, nameof(value)); } - } - - public MessageEncoderSettings MessageEncoderSettings - { - get { return _messageEncoderSettings; } - } - - public UpdateRequest Request - { - get { return _request; } - } - - public bool RetryRequested - { - get { return _retryRequested; } - set { _retryRequested = value; } - } - - public WriteConcern WriteConcern - { - get { return _writeConcern; } - set { _writeConcern = Ensure.IsNotNull(value, nameof(value)); } - } - - // public methods - public WriteConcernResult Execute(RetryableWriteContext context, CancellationToken cancellationToken) - { - Ensure.IsNotNull(context, nameof(context)); - - var operation = CreateOperation(); - BulkWriteOperationResult result; - MongoBulkWriteOperationException exception = null; - try - { - result = operation.Execute(context, cancellationToken); - } - catch (MongoBulkWriteOperationException ex) - { - result = ex.Result; - exception = ex; - } - - return CreateResultOrThrow(context.Channel, result, exception); - } - - public async Task ExecuteAsync(RetryableWriteContext context, CancellationToken cancellationToken) - { - Ensure.IsNotNull(context, nameof(context)); - - var operation = CreateOperation(); - BulkWriteOperationResult result; - MongoBulkWriteOperationException exception = null; - try - { - result = await operation.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); - } - catch (MongoBulkWriteOperationException ex) - { - result = ex.Result; - exception = ex; - } - - return CreateResultOrThrow(context.Channel, result, exception); - } - - // private methods - private BulkUpdateOperation CreateOperation() - { - var requests = new[] { _request }; - return new BulkUpdateOperation(_collectionNamespace, requests, _messageEncoderSettings) - { - BypassDocumentValidation = _bypassDocumentValidation, - IsOrdered = true, - RetryRequested = _retryRequested, - WriteConcern = _writeConcern - }; - } - - private WriteConcernResult CreateResultOrThrow(IChannelHandle channel, BulkWriteOperationResult result, MongoBulkWriteOperationException exception) - { - var converter = new BulkWriteOperationResultConverter(); - if (exception != null) - { - throw converter.ToWriteConcernException(channel.ConnectionDescription.ConnectionId, exception); - } - else - { - if (_writeConcern.IsAcknowledged) - { - return converter.ToWriteConcernResult(result); - } - else - { - return null; - } - } - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/InsertOpcodeOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/InsertOpcodeOperationTests.cs deleted file mode 100644 index bd8ff43b2f5..00000000000 --- a/tests/MongoDB.Driver.Tests/Core/Operations/InsertOpcodeOperationTests.cs +++ /dev/null @@ -1,176 +0,0 @@ -/* Copyright 2013-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Collections.Generic; -using FluentAssertions; -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Serializers; -using MongoDB.TestHelpers.XunitExtensions; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.TestHelpers.XunitExtensions; -using Xunit; - -namespace MongoDB.Driver.Core.Operations -{ - public class InsertOpcodeOperationTests : OperationTestBase - { - private BsonDocument[] _documents; - - public InsertOpcodeOperationTests() - { - _documents = new[] - { - BsonDocument.Parse("{_id: 1, x: 1}") - }; - } - - [Fact] - public void Constructor_should_throw_when_collection_namespace_is_null() - { - Action act = () => new InsertOpcodeOperation(null, _documents, BsonDocumentSerializer.Instance, _messageEncoderSettings); - - act.ShouldThrow(); - } - - [Fact] - public void Constructor_should_throw_when_serializer_is_null() - { - Action act = () => new InsertOpcodeOperation(_collectionNamespace, _documents, null, _messageEncoderSettings); - - act.ShouldThrow(); - } - - [Fact] - public void Constructor_should_throw_when_message_encoder_settings_is_null() - { - Action act = () => new InsertOpcodeOperation(_collectionNamespace, _documents, BsonDocumentSerializer.Instance, null); - - act.ShouldThrow(); - } - - [Fact] - public void Constructor_should_initialize_object() - { - var subject = new InsertOpcodeOperation(_collectionNamespace, _documents, BsonDocumentSerializer.Instance, _messageEncoderSettings); - - subject.CollectionNamespace.FullName.Should().Be(_collectionNamespace.FullName); - subject.Documents.Count.Should().Be(_documents.Length); - subject.Serializer.Should().BeSameAs(BsonDocumentSerializer.Instance); - subject.MessageEncoderSettings.Should().BeEquivalentTo(_messageEncoderSettings); - } - - [Fact] - public void ContinueOnError_should_work() - { - var subject = new InsertOpcodeOperation(_collectionNamespace, _documents, BsonDocumentSerializer.Instance, _messageEncoderSettings); - - subject.ContinueOnError.Should().Be(false); - - subject.ContinueOnError = true; - - subject.ContinueOnError.Should().Be(true); - } - - [Fact] - public void MaxBatchCount_should_work() - { - var subject = new InsertOpcodeOperation(_collectionNamespace, _documents, BsonDocumentSerializer.Instance, _messageEncoderSettings); - - subject.MaxBatchCount.Should().Be(null); - - subject.MaxBatchCount = 20; - - subject.MaxBatchCount.Should().Be(20); - } - - [Fact] - public void MaxDocumentSize_should_work() - { - var subject = new InsertOpcodeOperation(_collectionNamespace, _documents, BsonDocumentSerializer.Instance, _messageEncoderSettings); - - subject.MaxDocumentSize.Should().Be(null); - - subject.MaxDocumentSize = 20; - - subject.MaxDocumentSize.Should().Be(20); - } - - [Fact] - public void MaxMessageSize_should_work() - { - var subject = new InsertOpcodeOperation(_collectionNamespace, _documents, BsonDocumentSerializer.Instance, _messageEncoderSettings); - - subject.MaxMessageSize.Should().Be(null); - - subject.MaxMessageSize = 20; - - subject.MaxMessageSize.Should().Be(20); - } - - [Fact] - public void WriteConcern_should_work() - { - var subject = new InsertOpcodeOperation(_collectionNamespace, _documents, BsonDocumentSerializer.Instance, _messageEncoderSettings); - - subject.WriteConcern.Should().Be(WriteConcern.Acknowledged); - - subject.WriteConcern = WriteConcern.W2; - - subject.WriteConcern.Should().Be(WriteConcern.W2); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_insert_a_single_document( - [Values(false, true)] - bool async) - { - RequireServer.Check(); - DropCollection(); - var subject = new InsertOpcodeOperation(_collectionNamespace, _documents, BsonDocumentSerializer.Instance, _messageEncoderSettings); - - var result = ExecuteOperation(subject, async); - result.Should().HaveCount(1); - - var list = ReadAllFromCollection(async); - list.Should().HaveCount(1); - } - - [Theory] - [ParameterAttributeData] - public void Execute_should_insert_multiple_documents( - [Values(false, true)] - bool async) - { - RequireServer.Check(); - DropCollection(); - var documents = new[] - { - BsonDocument.Parse("{_id: 1, x: 1}"), - BsonDocument.Parse("{_id: 2, x: 2}"), - BsonDocument.Parse("{_id: 3, x: 3}"), - BsonDocument.Parse("{_id: 4, x: 4}"), - }; - var subject = new InsertOpcodeOperation(_collectionNamespace, documents, BsonDocumentSerializer.Instance, _messageEncoderSettings); - - var result = ExecuteOperation(subject, async); - result.Should().HaveCount(1); - - var list = ReadAllFromCollection(async); - list.Should().HaveCount(4); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/UpdateOpcodeOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/UpdateOpcodeOperationTests.cs deleted file mode 100644 index 150eaa56d90..00000000000 --- a/tests/MongoDB.Driver.Tests/Core/Operations/UpdateOpcodeOperationTests.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright 2020-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using FluentAssertions; -using MongoDB.Bson; -using MongoDB.TestHelpers.XunitExtensions; -using MongoDB.Driver.Core.Misc; -using Xunit; - -namespace MongoDB.Driver.Core.Operations -{ - public class UpdateOpcodeOperationTests : OperationTestBase - { - [Theory] - [ParameterAttributeData] - public void Execute_with_hint_should_throw_when_hint_is_not_supported( - [Values(0, 1)] int w, - [Values(false, true)] bool async) - { - var writeConcern = new WriteConcern(w); - var request = new UpdateRequest( - UpdateType.Update, - new BsonDocument("x", 1), - new BsonDocument("$set", new BsonDocument("x", 2))) - { - Hint = new BsonDocument("_id", 1) - }; - var subject = new UpdateOpcodeOperation(_collectionNamespace, request, _messageEncoderSettings) - { - WriteConcern = writeConcern - }; - - var exception = Record.Exception(() => ExecuteOperation(subject, async)); - - if (!writeConcern.IsAcknowledged) - { - exception.Should().BeOfType(); - } -#pragma warning disable CS0618 // Type or member is obsolete - else if (Feature.HintForUpdateAndReplaceOperations.IsSupported(CoreTestConfiguration.MaxWireVersion)) -#pragma warning restore CS0618 // Type or member is obsolete - { - exception.Should().BeNull(); - } - else - { - exception.Should().BeOfType(); - } - } - } -} From dde1ef988810eb4322e5afdab3c0421b2428c2cd Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Tue, 27 May 2025 12:41:53 -0700 Subject: [PATCH 09/24] CSHARP-5598: Fix CoreServerSessionPool.IsAboutToExpire test (#1699) --- .../Bindings/CoreServerSessionPoolTests.cs | 63 ++++--------------- 1 file changed, 11 insertions(+), 52 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/CoreServerSessionPoolTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/CoreServerSessionPoolTests.cs index be7a3b9d3ca..03be5c55ff0 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/CoreServerSessionPoolTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/CoreServerSessionPoolTests.cs @@ -18,16 +18,10 @@ using System.Linq; using System.Net; using System.Reflection; -using System.Threading; -using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; -using MongoDB.Driver.Core.Clusters.ServerSelectors; -using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; -using MongoDB.Driver.Encryption; using Moq; using Xunit; @@ -205,35 +199,23 @@ public void ReleaseSession_should_have_expected_result(int[] pooledSessionWasRec } } - [Fact] - public void IsAboutToExpire_should_never_expire_in_load_balancing_mode() - { - var subject = CreateSubject(); - var mockedCluster = new TestCluster(ClusterType.LoadBalanced); - var mockedServerSessionPool = new CoreServerSessionPool(mockedCluster); - var mockSession = new Mock(); - var lastUsedAt = DateTime.UtcNow.AddSeconds(1741); - mockSession.SetupGet(m => m.LastUsedAt).Returns(lastUsedAt); - - var result = mockedServerSessionPool.IsAboutToExpire(mockSession.Object); - - result.Should().BeFalse(); - } - [Theory] - [InlineData(null, true)] - [InlineData(1741, true)] - [InlineData(1739, false)] - public void IsAboutToExpire_should_return_expected_result(int? lastUsedSecondsAgo, bool expectedResult) + [InlineData(ClusterType.Sharded, null, true)] + [InlineData(ClusterType.Sharded, 1741, true)] + [InlineData(ClusterType.Sharded, 1739, false)] + [InlineData(ClusterType.LoadBalanced, null, false)] + [InlineData(ClusterType.LoadBalanced, 1741, false)] + [InlineData(ClusterType.LoadBalanced, 1739, false)] + public void IsAboutToExpire_should_return_expected_result(ClusterType clusterType, int? lastUsedSecondsAgo, bool isAboutToExpire) { - var subject = CreateSubject(); + var subject = CreateSubject(clusterType); var mockSession = new Mock(); var lastUsedAt = lastUsedSecondsAgo == null ? (DateTime?)null : DateTime.UtcNow.AddSeconds(-lastUsedSecondsAgo.Value); mockSession.SetupGet(m => m.LastUsedAt).Returns(lastUsedAt); var result = subject.IsAboutToExpire(mockSession.Object); - result.Should().Be(expectedResult); + result.Should().Be(isAboutToExpire); } // private methods @@ -256,7 +238,7 @@ private Mock CreateMockSession(bool recentlyUsed) return recentlyUsed ? CreateMockRecentlyUsedSession() : CreateMockExpiredSession(); } - private CoreServerSessionPool CreateSubject() + private CoreServerSessionPool CreateSubject(ClusterType clusterType = ClusterType.Sharded) { var clusterId = new ClusterId(); var endPoint = new DnsEndPoint("localhost", 27017); @@ -270,36 +252,13 @@ private CoreServerSessionPool CreateSubject() version: new SemanticVersion(3, 6, 0), wireVersionRange: new Range(6, 14)); - var clusterDescription = new ClusterDescription(clusterId, false, null, ClusterType.Sharded, [serverDescription]); + var clusterDescription = new ClusterDescription(clusterId, false, null, clusterType, [serverDescription]); var mockCluster = new Mock(); mockCluster.SetupGet(m => m.Description).Returns(clusterDescription); return new CoreServerSessionPool(mockCluster.Object); } - - private class TestCluster : IClusterInternal - { - public TestCluster(ClusterType clusterType) - { - Description = new ClusterDescription(new ClusterId(), false, null, clusterType, Enumerable.Empty()); - } - - public ClusterId ClusterId => throw new NotImplementedException(); - - public ClusterDescription Description { get; } - - public ClusterSettings Settings => throw new NotImplementedException(); - - public event EventHandler DescriptionChanged; - - public ICoreServerSession AcquireServerSession() => throw new NotImplementedException(); - public void Dispose() => throw new NotImplementedException(); - public void Initialize() => DescriptionChanged?.Invoke(this, new ClusterDescriptionChangedEventArgs(Description, Description)); - public IServer SelectServer(IServerSelector selector, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task SelectServerAsync(IServerSelector selector, CancellationToken cancellationToken) => throw new NotImplementedException(); - public ICoreSessionHandle StartSession(CoreSessionOptions options = null) => throw new NotImplementedException(); - } } internal static class CoreServerSessionPoolReflector From b2f1180919cad6d033f8980cfe8a7e2e3cc69caa Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Thu, 29 May 2025 15:46:06 -0700 Subject: [PATCH 10/24] CSHARP-3662: MongoClientSettings.SocketTimeout does not work for values under 500ms on Windows for sync code (#1690) --- .../Core/Compression/SnappyCompressor.cs | 4 +- .../Core/Connections/BinaryConnection.cs | 8 +- .../Core/Connections/TcpStreamFactory.cs | 12 +- .../Core/Misc/StreamExtensionMethods.cs | 182 ++++++++++++----- .../Core/Misc/TaskExtensions.cs | 72 +++++++ .../Core/Connections/BinaryConnectionTests.cs | 121 +++++++---- .../Core/Misc/TaskExtensionsTests.cs | 191 ++++++++++++++++++ .../Jira/CSharp3188Tests.cs | 31 +-- .../Specifications/UnifiedTestSpecRunner.cs | 3 - .../ServerDiscoveryAndMonitoringProseTests.cs | 2 +- 10 files changed, 497 insertions(+), 129 deletions(-) create mode 100644 tests/MongoDB.Driver.Tests/Core/Misc/TaskExtensionsTests.cs diff --git a/src/MongoDB.Driver/Core/Compression/SnappyCompressor.cs b/src/MongoDB.Driver/Core/Compression/SnappyCompressor.cs index dbb11f48e59..7419244a46c 100644 --- a/src/MongoDB.Driver/Core/Compression/SnappyCompressor.cs +++ b/src/MongoDB.Driver/Core/Compression/SnappyCompressor.cs @@ -34,7 +34,7 @@ public void Compress(Stream input, Stream output) { var uncompressedSize = (int)(input.Length - input.Position); var uncompressedBytes = new byte[uncompressedSize]; // does not include uncompressed message headers - input.ReadBytes(uncompressedBytes, offset: 0, count: uncompressedSize, CancellationToken.None); + input.ReadBytes(uncompressedBytes, offset: 0, count: uncompressedSize, Timeout.InfiniteTimeSpan, CancellationToken.None); var maxCompressedSize = Snappy.GetMaxCompressedLength(uncompressedSize); var compressedBytes = new byte[maxCompressedSize]; var compressedSize = Snappy.Compress(uncompressedBytes, compressedBytes); @@ -50,7 +50,7 @@ public void Decompress(Stream input, Stream output) { var compressedSize = (int)(input.Length - input.Position); var compressedBytes = new byte[compressedSize]; - input.ReadBytes(compressedBytes, offset: 0, count: compressedSize, CancellationToken.None); + input.ReadBytes(compressedBytes, offset: 0, count: compressedSize, Timeout.InfiniteTimeSpan, CancellationToken.None); var uncompressedSize = Snappy.GetUncompressedLength(compressedBytes); var decompressedBytes = new byte[uncompressedSize]; var decompressedSize = Snappy.Decompress(compressedBytes, decompressedBytes); diff --git a/src/MongoDB.Driver/Core/Connections/BinaryConnection.cs b/src/MongoDB.Driver/Core/Connections/BinaryConnection.cs index 380526e8348..d026da63a5e 100644 --- a/src/MongoDB.Driver/Core/Connections/BinaryConnection.cs +++ b/src/MongoDB.Driver/Core/Connections/BinaryConnection.cs @@ -333,14 +333,15 @@ private IByteBuffer ReceiveBuffer(CancellationToken cancellationToken) try { var messageSizeBytes = new byte[4]; - _stream.ReadBytes(messageSizeBytes, 0, 4, cancellationToken); + var readTimeout = _stream.CanTimeout ? TimeSpan.FromMilliseconds(_stream.ReadTimeout) : Timeout.InfiniteTimeSpan; + _stream.ReadBytes(messageSizeBytes, 0, 4, readTimeout, cancellationToken); var messageSize = BinaryPrimitives.ReadInt32LittleEndian(messageSizeBytes); EnsureMessageSizeIsValid(messageSize); var inputBufferChunkSource = new InputBufferChunkSource(BsonChunkPool.Default); var buffer = ByteBufferFactory.Create(inputBufferChunkSource, messageSize); buffer.Length = messageSize; buffer.SetBytes(0, messageSizeBytes, 0, 4); - _stream.ReadBytes(buffer, 4, messageSize - 4, cancellationToken); + _stream.ReadBytes(buffer, 4, messageSize - 4, readTimeout, cancellationToken); _lastUsedAtUtc = DateTime.UtcNow; buffer.MakeReadOnly(); return buffer; @@ -535,7 +536,8 @@ private void SendBuffer(IByteBuffer buffer, CancellationToken cancellationToken) try { - _stream.WriteBytes(buffer, 0, buffer.Length, cancellationToken); + var writeTimeout = _stream.CanTimeout ? TimeSpan.FromMilliseconds(_stream.WriteTimeout) : Timeout.InfiniteTimeSpan; + _stream.WriteBytes(buffer, 0, buffer.Length, writeTimeout, cancellationToken); _lastUsedAtUtc = DateTime.UtcNow; } catch (Exception ex) diff --git a/src/MongoDB.Driver/Core/Connections/TcpStreamFactory.cs b/src/MongoDB.Driver/Core/Connections/TcpStreamFactory.cs index ae2c1fb69b4..9bb93eea097 100644 --- a/src/MongoDB.Driver/Core/Connections/TcpStreamFactory.cs +++ b/src/MongoDB.Driver/Core/Connections/TcpStreamFactory.cs @@ -138,7 +138,10 @@ private void Connect(Socket socket, EndPoint endPoint, CancellationToken cancell if (!connectOperation.IsCompleted) { - try { socket.Dispose(); } catch { } + try + { + socket.Dispose(); + } catch { } cancellationToken.ThrowIfCancellationRequested(); throw new TimeoutException($"Timed out connecting to {endPoint}. Timeout was {_settings.ConnectTimeout}."); @@ -164,7 +167,12 @@ private async Task ConnectAsync(Socket socket, EndPoint endPoint, CancellationTo if (!connectTask.IsCompleted) { - try { socket.Dispose(); } catch { } + try + { + socket.Dispose(); + // should await on the read task to avoid UnobservedTaskException + await connectTask.ConfigureAwait(false); + } catch { } cancellationToken.ThrowIfCancellationRequested(); throw new TimeoutException($"Timed out connecting to {endPoint}. Timeout was {_settings.ConnectTimeout}."); diff --git a/src/MongoDB.Driver/Core/Misc/StreamExtensionMethods.cs b/src/MongoDB.Driver/Core/Misc/StreamExtensionMethods.cs index 248aa756272..1cb4cd5181f 100644 --- a/src/MongoDB.Driver/Core/Misc/StreamExtensionMethods.cs +++ b/src/MongoDB.Driver/Core/Misc/StreamExtensionMethods.cs @@ -36,46 +36,80 @@ public static void EfficientCopyTo(this Stream input, Stream output) } } - public static async Task ReadAsync(this Stream stream, byte[] buffer, int offset, int count, TimeSpan timeout, CancellationToken cancellationToken) + public static int Read(this Stream stream, byte[] buffer, int offset, int count, TimeSpan timeout, CancellationToken cancellationToken) { - var state = 1; // 1 == reading, 2 == done reading, 3 == timedout, 4 == cancelled - - var bytesRead = 0; - using (new Timer(_ => ChangeState(3), null, timeout, Timeout.InfiniteTimeSpan)) - using (cancellationToken.Register(() => ChangeState(4))) + try { - try - { - bytesRead = await stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); - ChangeState(2); // note: might not actually go to state 2 if already in state 3 or 4 - } - catch when (state == 1) - { - try { stream.Dispose(); } catch { } - throw; - } - catch when (state >= 3) + using var manualResetEvent = new ManualResetEventSlim(); + var readOperation = stream.BeginRead( + buffer, + offset, + count, + state => ((ManualResetEventSlim)state.AsyncState).Set(), + manualResetEvent); + + if (readOperation.IsCompleted || manualResetEvent.Wait(timeout, cancellationToken)) { - // a timeout or operation cancelled exception will be thrown instead + return stream.EndRead(readOperation); } + } + catch (OperationCanceledException) + { + // Have to suppress OperationCanceledException here, it will be thrown after the stream will be disposed. + } + catch (ObjectDisposedException) + { + throw new IOException(); + } - if (state == 3) { throw new TimeoutException(); } - if (state == 4) { throw new OperationCanceledException(); } + try + { + stream.Dispose(); + } + catch + { + // Ignore any exceptions } - return bytesRead; + cancellationToken.ThrowIfCancellationRequested(); + throw new TimeoutException(); + } - void ChangeState(int to) + public static async Task ReadAsync(this Stream stream, byte[] buffer, int offset, int count, TimeSpan timeout, CancellationToken cancellationToken) + { + Task readTask = null; + try + { + readTask = stream.ReadAsync(buffer, offset, count); + return await readTask.WaitAsync(timeout, cancellationToken).ConfigureAwait(false); + } + catch (ObjectDisposedException) { - var from = Interlocked.CompareExchange(ref state, to, 1); - if (from == 1 && to >= 3) + // It's possible to get ObjectDisposedException when the connection pool was closed with interruptInUseConnections set to true. + throw new IOException(); + } + catch (Exception ex) when (ex is OperationCanceledException or TimeoutException) + { + // await Task.WaitAsync() throws OperationCanceledException in case of cancellation and TimeoutException in case of timeout + try { - try { stream.Dispose(); } catch { } // disposing the stream aborts the read attempt + stream.Dispose(); + if (readTask != null) + { + // Should await on the task to avoid UnobservedTaskException + await readTask.ConfigureAwait(false); + } } + catch + { + // Ignore any exceptions + } + + throw; } } - public static void ReadBytes(this Stream stream, byte[] buffer, int offset, int count, CancellationToken cancellationToken) + public static void ReadBytes(this Stream stream, byte[] buffer, int offset, int count, TimeSpan timeout, CancellationToken cancellationToken) { Ensure.IsNotNull(stream, nameof(stream)); Ensure.IsNotNull(buffer, nameof(buffer)); @@ -84,7 +118,7 @@ public static void ReadBytes(this Stream stream, byte[] buffer, int offset, int while (count > 0) { - var bytesRead = stream.Read(buffer, offset, count); // TODO: honor cancellationToken? + var bytesRead = stream.Read(buffer, offset, count, timeout, cancellationToken); if (bytesRead == 0) { throw new EndOfStreamException(); @@ -94,7 +128,7 @@ public static void ReadBytes(this Stream stream, byte[] buffer, int offset, int } } - public static void ReadBytes(this Stream stream, IByteBuffer buffer, int offset, int count, CancellationToken cancellationToken) + public static void ReadBytes(this Stream stream, IByteBuffer buffer, int offset, int count, TimeSpan timeout, CancellationToken cancellationToken) { Ensure.IsNotNull(stream, nameof(stream)); Ensure.IsNotNull(buffer, nameof(buffer)); @@ -105,7 +139,7 @@ public static void ReadBytes(this Stream stream, IByteBuffer buffer, int offset, { var backingBytes = buffer.AccessBackingBytes(offset); var bytesToRead = Math.Min(count, backingBytes.Count); - var bytesRead = stream.Read(backingBytes.Array, backingBytes.Offset, bytesToRead); // TODO: honor cancellationToken? + var bytesRead = stream.Read(backingBytes.Array, backingBytes.Offset, bytesToRead, timeout, cancellationToken); if (bytesRead == 0) { throw new EndOfStreamException(); @@ -155,44 +189,82 @@ public static async Task ReadBytesAsync(this Stream stream, IByteBuffer buffer, } } - - public static async Task WriteAsync(this Stream stream, byte[] buffer, int offset, int count, TimeSpan timeout, CancellationToken cancellationToken) + public static void Write(this Stream stream, byte[] buffer, int offset, int count, TimeSpan timeout, CancellationToken cancellationToken) { - var state = 1; // 1 == writing, 2 == done writing, 3 == timedout, 4 == cancelled - - using (new Timer(_ => ChangeState(3), null, timeout, Timeout.InfiniteTimeSpan)) - using (cancellationToken.Register(() => ChangeState(4))) + try { - try - { - await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); - ChangeState(2); // note: might not actually go to state 2 if already in state 3 or 4 - } - catch when (state == 1) - { - try { stream.Dispose(); } catch { } - throw; - } - catch when (state >= 3) + using var manualResetEvent = new ManualResetEventSlim(); + var writeOperation = stream.BeginWrite( + buffer, + offset, + count, + state => ((ManualResetEventSlim)state.AsyncState).Set(), + manualResetEvent); + + if (writeOperation.IsCompleted || manualResetEvent.Wait(timeout, cancellationToken)) { - // a timeout or operation cancelled exception will be thrown instead + stream.EndWrite(writeOperation); + return; } + } + catch (OperationCanceledException) + { + // Have to suppress OperationCanceledException here, it will be thrown after the stream will be disposed. + } + catch (ObjectDisposedException) + { + // It's possible to get ObjectDisposedException when the connection pool was closed with interruptInUseConnections set to true. + throw new IOException(); + } - if (state == 3) { throw new TimeoutException(); } - if (state == 4) { throw new OperationCanceledException(); } + try + { + stream.Dispose(); } + catch + { + // Ignore any exceptions + } + + cancellationToken.ThrowIfCancellationRequested(); + throw new TimeoutException(); + } - void ChangeState(int to) + public static async Task WriteAsync(this Stream stream, byte[] buffer, int offset, int count, TimeSpan timeout, CancellationToken cancellationToken) + { + Task writeTask = null; + try + { + writeTask = stream.WriteAsync(buffer, offset, count); + await writeTask.WaitAsync(timeout, cancellationToken).ConfigureAwait(false); + } + catch (ObjectDisposedException) + { + // It's possible to get ObjectDisposedException when the connection pool was closed with interruptInUseConnections set to true. + throw new IOException(); + } + catch (Exception ex) when (ex is OperationCanceledException or TimeoutException) { - var from = Interlocked.CompareExchange(ref state, to, 1); - if (from == 1 && to >= 3) + // await Task.WaitAsync() throws OperationCanceledException in case of cancellation and TimeoutException in case of timeout + try { - try { stream.Dispose(); } catch { } // disposing the stream aborts the write attempt + stream.Dispose(); + // Should await on the task to avoid UnobservedTaskException + if (writeTask != null) + { + await writeTask.ConfigureAwait(false); + } } + catch + { + // Ignore any exceptions + } + + throw; } } - public static void WriteBytes(this Stream stream, IByteBuffer buffer, int offset, int count, CancellationToken cancellationToken) + public static void WriteBytes(this Stream stream, IByteBuffer buffer, int offset, int count, TimeSpan timeout, CancellationToken cancellationToken) { Ensure.IsNotNull(stream, nameof(stream)); Ensure.IsNotNull(buffer, nameof(buffer)); @@ -204,7 +276,7 @@ public static void WriteBytes(this Stream stream, IByteBuffer buffer, int offset cancellationToken.ThrowIfCancellationRequested(); var backingBytes = buffer.AccessBackingBytes(offset); var bytesToWrite = Math.Min(count, backingBytes.Count); - stream.Write(backingBytes.Array, backingBytes.Offset, bytesToWrite); // TODO: honor cancellationToken? + stream.Write(backingBytes.Array, backingBytes.Offset, bytesToWrite, timeout, cancellationToken); offset += bytesToWrite; count -= bytesToWrite; } diff --git a/src/MongoDB.Driver/Core/Misc/TaskExtensions.cs b/src/MongoDB.Driver/Core/Misc/TaskExtensions.cs index b6702bce182..0de05faa0e5 100644 --- a/src/MongoDB.Driver/Core/Misc/TaskExtensions.cs +++ b/src/MongoDB.Driver/Core/Misc/TaskExtensions.cs @@ -15,12 +15,84 @@ using System; using System.Runtime.CompilerServices; +using System.Threading; using System.Threading.Tasks; namespace MongoDB.Driver.Core.Misc { internal static class TaskExtensions { +#if !NET6_0_OR_GREATER + public static Task WaitAsync(this Task task, TimeSpan timeout, CancellationToken cancellationToken) + { + EnsureTimeoutIsValid(timeout); + return WaitAsyncCore(task, timeout, cancellationToken); + + static async Task WaitAsyncCore(Task task, TimeSpan timeout, CancellationToken cancellationToken) + { + if (!task.IsCompleted) + { + var timeoutTask = Task.Delay(timeout, cancellationToken); + await Task.WhenAny(task, timeoutTask).ConfigureAwait(false); + } + + if (task.IsCompleted) + { + // will re-throw the exception if any + await task.ConfigureAwait(false); + return; + } + + if (cancellationToken.IsCancellationRequested) + { + throw new TaskCanceledException(); + } + throw new TimeoutException(); + } + } + + public static Task WaitAsync(this Task task, TimeSpan timeout, CancellationToken cancellationToken) + { + EnsureTimeoutIsValid(timeout); + return WaitAsyncCore(task, timeout, cancellationToken); + + static async Task WaitAsyncCore(Task task, TimeSpan timeout, CancellationToken cancellationToken) + { + if (!task.IsCompleted) + { + var timeoutTask = Task.Delay(timeout, cancellationToken); + await Task.WhenAny(task, timeoutTask).ConfigureAwait(false); + } + + if (task.IsCompleted) + { + // will return the result or re-throw the exception if any + return await task.ConfigureAwait(false); + } + + if (cancellationToken.IsCancellationRequested) + { + throw new TaskCanceledException(); + } + + throw new TimeoutException(); + } + } + + private static void EnsureTimeoutIsValid(TimeSpan timeout) + { + if (timeout == Timeout.InfiniteTimeSpan) + { + return; + } + + if (timeout < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } + } +#endif + internal struct YieldNoContextAwaitable { public YieldNoContextAwaiter GetAwaiter() { return new YieldNoContextAwaiter(); } diff --git a/tests/MongoDB.Driver.Tests/Core/Connections/BinaryConnectionTests.cs b/tests/MongoDB.Driver.Tests/Core/Connections/BinaryConnectionTests.cs index 9a904c3db08..c183dfba024 100644 --- a/tests/MongoDB.Driver.Tests/Core/Connections/BinaryConnectionTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Connections/BinaryConnectionTests.cs @@ -14,7 +14,6 @@ */ using System; -using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Sockets; @@ -153,10 +152,10 @@ public async Task Open_should_create_authenticators_only_once( var mockStreamFactory = new Mock(); using var stream = new IgnoreWritesMemoryStream(memoryStream.ToArray()); mockStreamFactory - .Setup(s => s.CreateStream(It.IsAny(), CancellationToken.None)) + .Setup(s => s.CreateStream(It.IsAny(), It.IsAny())) .Returns(stream); mockStreamFactory - .Setup(s => s.CreateStreamAsync(It.IsAny(), CancellationToken.None)) + .Setup(s => s.CreateStreamAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(stream); var connectionInitializer = new ConnectionInitializer( @@ -620,10 +619,8 @@ public void ReceiveMessage_should_handle_out_of_order_replies( } } - [Theory] - [ParameterAttributeData] - public void ReceiveMessage_should_not_produce_unobserved_task_exceptions_on_fail( - [Values(false, true)] bool async) + [Fact] + public async Task ReceiveMessage_should_not_produce_unobserved_task_exceptions_on_fail() { var unobservedTaskExceptionRaised = false; var mockStream = new Mock(); @@ -644,30 +641,13 @@ public void ReceiveMessage_should_not_produce_unobserved_task_exceptions_on_fail .Setup(f => f.CreateStream(_endPoint, CancellationToken.None)) .Returns(mockStream.Object); - if (async) - { - mockStream - .Setup(s => s.ReadAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Throws(new SocketException()); - } - else - { - mockStream - .Setup(s => s.Read(It.IsAny(), It.IsAny(), It.IsAny())) - .Throws(new SocketException()); - } + var tcs = new TaskCompletionSource(); + tcs.SetException(new SocketException()); + SetupStreamRead(mockStream, tcs); _subject.Open(CancellationToken.None); - Exception exception; - if (async) - { - exception = Record.Exception(() => _subject.ReceiveMessageAsync(1, encoderSelector, _messageEncoderSettings, CancellationToken.None).GetAwaiter().GetResult()); - } - else - { - exception = Record.Exception(() => _subject.ReceiveMessage(1, encoderSelector, _messageEncoderSettings, CancellationToken.None)); - } + var exception = await Record.ExceptionAsync(() => _subject.ReceiveMessageAsync(1, encoderSelector, _messageEncoderSettings, CancellationToken.None)); exception.Should().BeOfType(); GC.Collect(); // Collects the unobserved tasks @@ -682,6 +662,53 @@ public void ReceiveMessage_should_not_produce_unobserved_task_exceptions_on_fail } } + [Fact] + public async Task ReceiveMessageAsync_should_not_produce_unobserved_task_exceptions_on_timeout() + { + GC.Collect(); // Collects the unobserved tasks + GC.WaitForPendingFinalizers(); // Assures finalizers are executed + + Exception ex = null; + var mockStream = new Mock(); + EventHandler eventHandler = (s, args) => + { + ex = args.Exception; + }; + + try + { + TaskScheduler.UnobservedTaskException += eventHandler; + var encoderSelector = new ReplyMessageEncoderSelector(BsonDocumentSerializer.Instance); + + _mockStreamFactory + .Setup(f => f.CreateStream(_endPoint, CancellationToken.None)) + .Returns(mockStream.Object); + + var tcs = new TaskCompletionSource(); + SetupStreamRead(mockStream, tcs, 50); + _subject.Open(CancellationToken.None); + + var exception = await Record.ExceptionAsync(() => _subject.ReceiveMessageAsync(1, encoderSelector, _messageEncoderSettings, CancellationToken.None)); + exception.Should().BeOfType(); + exception.InnerException.Should().BeOfType(); + + tcs = null; + mockStream.Reset(); + GC.Collect(); // Collects the unobserved tasks + GC.WaitForPendingFinalizers(); // Assures finalizers are executed + + if (ex != null) + { + Assert.Fail($"{ex.Message} - {ex}"); + } + } + finally + { + TaskScheduler.UnobservedTaskException -= eventHandler; + mockStream.Object?.Dispose(); + } + } + [Theory] [ParameterAttributeData] public void ReceiveMessage_should_throw_network_exception_to_all_awaiters( @@ -698,10 +725,7 @@ public void ReceiveMessage_should_throw_network_exception_to_all_awaiters( _mockStreamFactory.Setup(f => f.CreateStream(_endPoint, CancellationToken.None)) .Returns(mockStream.Object); var readTcs = new TaskCompletionSource(); - mockStream.Setup(s => s.Read(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(() => readTcs.Task.GetAwaiter().GetResult()); - mockStream.Setup(s => s.ReadAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(readTcs.Task); + SetupStreamRead(mockStream, readTcs, readTimeoutMs: Timeout.Infinite); _subject.Open(CancellationToken.None); _capturedEvents.Clear(); @@ -763,10 +787,7 @@ public void ReceiveMessage_should_throw_MongoConnectionClosedException_when_conn .Returns(mockStream.Object); var readTcs = new TaskCompletionSource(); readTcs.SetException(new SocketException()); - mockStream.Setup(s => s.Read(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(() => readTcs.Task.GetAwaiter().GetResult()); - mockStream.Setup(s => s.ReadAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(readTcs.Task); + SetupStreamRead(mockStream, readTcs); _subject.Open(CancellationToken.None); _capturedEvents.Clear(); @@ -882,7 +903,7 @@ public void SendMessages_should_put_the_messages_on_the_stream_and_raise_the_cor if (async) { _mockStreamFactory.Setup(f => f.CreateStreamAsync(_endPoint, CancellationToken.None)) - .Returns(Task.FromResult(stream)); + .ReturnsAsync(stream); _subject.OpenAsync(CancellationToken.None).GetAwaiter().GetResult(); _capturedEvents.Clear(); @@ -910,6 +931,32 @@ public void SendMessages_should_put_the_messages_on_the_stream_and_raise_the_cor } } + private void SetupStreamRead(Mock streamMock, TaskCompletionSource tcs, int readTimeoutMs = 1000) + { + if (readTimeoutMs > 0) + { + streamMock.SetupGet(s => s.CanTimeout).Returns(true); + streamMock.SetupGet(s => s.ReadTimeout).Returns(readTimeoutMs); + } + + streamMock.Setup(s => s.BeginRead(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((byte[] _, int __, int ___, AsyncCallback callback, object state) => + { + var innerTcs = new TaskCompletionSource(state); + tcs.Task.ContinueWith(t => + { + innerTcs.TrySetException(t.Exception.InnerException); + callback(innerTcs.Task); + }); + return innerTcs.Task; + }); + streamMock.Setup(s => s.EndRead(It.IsAny())) + .Returns(x => ((Task)x).GetAwaiter().GetResult()); + streamMock.Setup(s => s.ReadAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(tcs.Task); + streamMock.Setup(s => s.Close()).Callback(() => tcs.TrySetException(new ObjectDisposedException("stream"))); + } + // nested type private sealed class IgnoreWritesMemoryStream : MemoryStream { diff --git a/tests/MongoDB.Driver.Tests/Core/Misc/TaskExtensionsTests.cs b/tests/MongoDB.Driver.Tests/Core/Misc/TaskExtensionsTests.cs new file mode 100644 index 00000000000..f3799318b5b --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Core/Misc/TaskExtensionsTests.cs @@ -0,0 +1,191 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; + +namespace MongoDB.Driver.Core.Misc +{ + public class TaskExtensionsTests + { + [Theory] + [ParameterAttributeData] + public async Task WaitAsync_should_throw_on_negative_timeout([Values(true, false)] bool isPromiseTask) + { + var task = CreateSubject(isPromiseTask); + + var exception = await Record.ExceptionAsync(() => task.WaitAsync(TimeSpan.FromSeconds(-42), CancellationToken.None)); + + exception.Should().BeOfType(); + } + + [Theory] + [ParameterAttributeData] + public async Task WaitAsync_should_work_for_task([Values(true, false)] bool isPromiseTask) + { + var task = CreateSubject(isPromiseTask); + + await task.WaitAsync(Timeout.InfiniteTimeSpan, CancellationToken.None); + + task.IsCompleted.Should().BeTrue(); + } + + [Theory] + [ParameterAttributeData] + public async Task WaitAsync_should_rethrow_for_failed_task([Values(true, false)] bool isPromiseTask) + { + var ex = new InvalidOperationException(); + var task = CreateSubject(isPromiseTask, ex); + + var exception = await Record.ExceptionAsync(() => task.WaitAsync(Timeout.InfiniteTimeSpan, CancellationToken.None)); + + exception.Should().Be(ex); + } + + [Fact] + public async Task WaitAsync_should_throw_on_cancellation() + { + var task = CreateSubject(true); + using var cts = new CancellationTokenSource(5); + + var exception = await Record.ExceptionAsync(() => task.WaitAsync(Timeout.InfiniteTimeSpan, cts.Token)); + + task.IsCompleted.Should().BeFalse(); + exception.Should().BeOfType(); + } + + [Fact] + public async Task WaitAsync_should_throw_on_timeout() + { + var task = CreateSubject(true); + + var exception = await Record.ExceptionAsync(() => task.WaitAsync(TimeSpan.FromMilliseconds(5), CancellationToken.None)); + + task.IsCompleted.Should().BeFalse(); + exception.Should().BeOfType(); + } + + [Theory] + [ParameterAttributeData] + public async Task WaitAsyncTResult_should_throw_on_negative_timeout([Values(true, false)] bool isPromiseTask) + { + var task = CreateSubject(42, isPromiseTask); + + var exception = await Record.ExceptionAsync(() => task.WaitAsync(TimeSpan.FromSeconds(-42), CancellationToken.None)); + + exception.Should().BeOfType(); + } + + [Theory] + [ParameterAttributeData] + public async Task WaitAsyncTResult_should_not_throw_on_infinite_timeout([Values(true, false)] bool isPromiseTask) + { + var task = CreateSubject(42, isPromiseTask); + + await task.WaitAsync(Timeout.InfiniteTimeSpan, CancellationToken.None); + + task.IsCompleted.Should().BeTrue(); + } + + [Theory] + [ParameterAttributeData] + public async Task WaitAsyncTResult_should_work_for_task([Values(true, false)] bool isPromiseTask) + { + var task = CreateSubject(42, isPromiseTask); + + var result = await task.WaitAsync(Timeout.InfiniteTimeSpan, CancellationToken.None); + + result.Should().Be(42); + } + + [Theory] + [ParameterAttributeData] + public async Task WaitAsyncTResult_should_rethrow_for_failed_task([Values(true, false)] bool isPromiseTask) + { + var ex = new InvalidOperationException(); + var task = CreateSubject(42, isPromiseTask, ex); + + var exception = await Record.ExceptionAsync(() => task.WaitAsync(Timeout.InfiniteTimeSpan, CancellationToken.None)); + + exception.Should().Be(ex); + } + + [Fact] + public async Task WaitAsyncTResult_should_throw_on_cancellation() + { + var task = CreateSubject(42, true); + using var cts = new CancellationTokenSource(5); + + var exception = await Record.ExceptionAsync(() => task.WaitAsync(Timeout.InfiniteTimeSpan, cts.Token)); + + task.IsCompleted.Should().BeFalse(); + exception.Should().BeOfType(); + } + + [Fact] + public async Task WaitAsyncTResult_should_throw_on_timeout() + { + var task = CreateSubject(42, true); + + var exception = await Record.ExceptionAsync(() => task.WaitAsync(TimeSpan.FromMilliseconds(5), CancellationToken.None)); + + task.IsCompleted.Should().BeFalse(); + exception.Should().BeOfType(); + } + + private Task CreateSubject(bool isPromise, Exception exception = null) + { + if (exception == null) + { + return isPromise ? Task.Delay(50) : Task.CompletedTask; + } + + return isPromise ? + Task.Delay(50).ContinueWith(_ => throw exception) : + Task.FromException(exception); + } + + private Task CreateSubject(TResult result, bool isPromise, Exception exception = null) + { + var tcs = new TaskCompletionSource(); + if (isPromise) + { + Task.Delay(50).ContinueWith(_ => + { + if (exception == null) + { + tcs.TrySetResult(result); + } + else + { + tcs.SetException(exception); + } + }); + } + + if (exception == null) + { + return isPromise ? tcs.Task : Task.FromResult(result); + } + + return isPromise ? tcs.Task : Task.FromException(exception); + } + } +} + diff --git a/tests/MongoDB.Driver.Tests/Jira/CSharp3188Tests.cs b/tests/MongoDB.Driver.Tests/Jira/CSharp3188Tests.cs index 019ca0c425e..7e0d4cb75f2 100644 --- a/tests/MongoDB.Driver.Tests/Jira/CSharp3188Tests.cs +++ b/tests/MongoDB.Driver.Tests/Jira/CSharp3188Tests.cs @@ -66,33 +66,12 @@ public void Connection_timeout_should_throw_expected_exception([Values(false, tr .Limit(1) .Project(projectionDefinition); - if (async) - { - var exception = Record.Exception(() => collection.AggregateAsync(pipeline).GetAwaiter().GetResult()); + var exception = Record.Exception(() => collection.AggregateAsync(pipeline).GetAwaiter().GetResult()); - var mongoConnectionException = exception.Should().BeOfType().Subject; -#pragma warning disable CS0618 // Type or member is obsolete - mongoConnectionException.ContainsSocketTimeoutException.Should().BeFalse(); -#pragma warning restore CS0618 // Type or member is obsolete - mongoConnectionException.ContainsTimeoutException.Should().BeTrue(); - var baseException = GetBaseException(mongoConnectionException); - baseException.Should().BeOfType().Which.InnerException.Should().BeNull(); - } - else - { - var exception = Record.Exception(() => collection.Aggregate(pipeline)); - - var mongoConnectionException = exception.Should().BeOfType().Subject; -#pragma warning disable CS0618 // Type or member is obsolete - mongoConnectionException.ContainsSocketTimeoutException.Should().BeTrue(); -#pragma warning restore CS0618 // Type or member is obsolete - mongoConnectionException.ContainsTimeoutException.Should().BeTrue(); - var baseException = GetBaseException(mongoConnectionException); - var socketException = baseException.Should().BeOfType() - .Which.InnerException.Should().BeOfType().Subject; - socketException.SocketErrorCode.Should().Be(SocketError.TimedOut); - socketException.InnerException.Should().BeNull(); - } + var mongoConnectionException = exception.Should().BeOfType().Subject; + mongoConnectionException.ContainsTimeoutException.Should().BeTrue(); + var baseException = GetBaseException(mongoConnectionException); + baseException.Should().BeOfType().Which.InnerException.Should().BeNull(); } Exception GetBaseException(MongoConnectionException mongoConnectionException) diff --git a/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs index 17e9f36d60f..256d4ccb8d1 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs @@ -263,9 +263,6 @@ private static void RequireKmsMock() => "legacy hello without speculativeAuthenticate is always observed", // transactions - // Skipped because CSharp Driver has an issue with handling read timeout for sync code-path. CSHARP-3662 - "add RetryableWriteError and UnknownTransactionCommitResult labels to connection errors", - // CSHARP Driver does not comply with the requirement to throw in case explicit writeConcern were used, see CSHARP-5468 "client bulkWrite with writeConcern in a transaction causes a transaction error", ]); diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs index c95e14ee7c5..cebdf47395f 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs @@ -96,7 +96,7 @@ public void Heartbeat_should_be_emitted_before_connection_open() var mockStream = new Mock(); mockStream - .Setup(s => s.Write(It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(s => s.BeginWrite(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback(() => EnqueueEvent(HelloReceivedEvent)) .Throws(new Exception("Stream is closed.")); From 3ab64607b20cd3ce2cc447549f375a141562f040 Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Thu, 29 May 2025 16:55:09 -0700 Subject: [PATCH 11/24] CSHARP-4918: Release notes automation (#1701) --- .github/workflows/pr.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 0faa7e70dbe..e7081a09e5a 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -8,6 +8,7 @@ on: - edited - labeled - unlabeled + - synchronize jobs: pull-request-validation: From 4ecb1797acf9f15b19a9b4dfa5f2ab5f40153ae9 Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Fri, 30 May 2025 11:55:27 -0700 Subject: [PATCH 12/24] CSHARP-5585: Remove reliance on Evergreen instance profile credentials (#1702) --- evergreen/evergreen.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/evergreen/evergreen.yml b/evergreen/evergreen.yml index 7437891078c..15b07bd96eb 100644 --- a/evergreen/evergreen.yml +++ b/evergreen/evergreen.yml @@ -455,6 +455,10 @@ functions: params: shell: "bash" working_dir: mongo-csharp-driver + include_expansions_in_env: + - "AWS_ACCESS_KEY_ID" + - "AWS_SECRET_ACCESS_KEY" + - "AWS_SESSION_TOKEN" script: | . ${DRIVERS_TOOLS}/.evergreen/secrets_handling/setup-secrets.sh drivers/atlas_connect . evergreen/run-atlas-connectivity-tests.sh @@ -2185,6 +2189,10 @@ task_groups: binary: bash env: VAULT_NAME: ${VAULT_NAME} + include_expansions_in_env: + - "AWS_ACCESS_KEY_ID" + - "AWS_SECRET_ACCESS_KEY" + - "AWS_SESSION_TOKEN" args: - ${DRIVERS_TOOLS}/.evergreen/serverless/create-instance.sh - command: expansions.update From 66f355217c4370fb412517c5b1e00f05033311e5 Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Fri, 30 May 2025 16:14:05 -0700 Subject: [PATCH 13/24] CSHARP-5585: Remove reliance on Evergreen instance profile credentials (#1704) --- evergreen/evergreen.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/evergreen/evergreen.yml b/evergreen/evergreen.yml index 15b07bd96eb..1e9745160ef 100644 --- a/evergreen/evergreen.yml +++ b/evergreen/evergreen.yml @@ -318,6 +318,10 @@ functions: bootstrap-mongohoused: - command: shell.exec params: + include_expansions_in_env: + - "AWS_ACCESS_KEY_ID" + - "AWS_SECRET_ACCESS_KEY" + - "AWS_SESSION_TOKEN" script: | DRIVERS_TOOLS="${DRIVERS_TOOLS}" bash ${DRIVERS_TOOLS}/.evergreen/atlas_data_lake/pull-mongohouse-image.sh - command: shell.exec @@ -1262,6 +1266,7 @@ tasks: - name: atlas-data-lake-test commands: + - func: assume-ec2-role - func: bootstrap-mongohoused - func: run-atlas-data-lake-test @@ -2047,9 +2052,14 @@ task_groups: setup_group: - func: fetch-source - func: prepare-resources + - func: assume-ec2-role - command: subprocess.exec params: binary: bash + include_expansions_in_env: + - "AWS_ACCESS_KEY_ID" + - "AWS_SECRET_ACCESS_KEY" + - "AWS_SESSION_TOKEN" env: LAMBDA_STACK_NAME: dbx-csharp-lambda args: From 2d3e62f5f19d9bf7ea432392677b57a9a42c0269 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 3 Jun 2025 17:15:56 +0200 Subject: [PATCH 14/24] CSHARP4040: Fix bug when using field with same element name as discriminator (#1684) --- .../Serialization/BsonClassMap.cs | 17 ++++++- .../StandardDiscriminatorConvention.cs | 7 +-- .../StandardDiscriminatorConventionTests.cs | 2 +- .../Jira/CSharp4040Tests.cs | 47 +++++++++++++++++++ 4 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 tests/MongoDB.Driver.Tests/Jira/CSharp4040Tests.cs diff --git a/src/MongoDB.Bson/Serialization/BsonClassMap.cs b/src/MongoDB.Bson/Serialization/BsonClassMap.cs index 87239a33bf5..2781d218d6b 100644 --- a/src/MongoDB.Bson/Serialization/BsonClassMap.cs +++ b/src/MongoDB.Bson/Serialization/BsonClassMap.cs @@ -1323,10 +1323,25 @@ internal IDiscriminatorConvention GetDiscriminatorConvention() var discriminatorConvention = _discriminatorConvention; if (discriminatorConvention == null) { - // it's possible but harmless for multiple threads to do the discriminator convention lookukp at the same time + // it's possible but harmless for multiple threads to do the discriminator convention lookup at the same time discriminatorConvention = LookupDiscriminatorConvention(); _discriminatorConvention = discriminatorConvention; + + if (discriminatorConvention != null) + { + var conflictingMemberMap = _allMemberMaps.FirstOrDefault(memberMap => memberMap.ElementName == discriminatorConvention.ElementName); + + if (conflictingMemberMap != null) + { + var fieldOrProperty = conflictingMemberMap.MemberInfo is FieldInfo ? "field" : "property"; + + throw new BsonSerializationException( + $"The discriminator element name cannot be {discriminatorConvention.ElementName} " + + $"because it is already being used by the {fieldOrProperty} {conflictingMemberMap.MemberName} of type {_classType.FullName}"); + } + } } + return discriminatorConvention; IDiscriminatorConvention LookupDiscriminatorConvention() diff --git a/src/MongoDB.Bson/Serialization/Conventions/StandardDiscriminatorConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/StandardDiscriminatorConvention.cs index ca4d2cbc117..d2b042d1fc0 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/StandardDiscriminatorConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/StandardDiscriminatorConvention.cs @@ -39,14 +39,15 @@ public abstract class StandardDiscriminatorConvention : IDiscriminatorConvention /// The element name. protected StandardDiscriminatorConvention(string elementName) { - if (elementName == null) + if (string.IsNullOrEmpty(elementName)) { - throw new ArgumentNullException("elementName"); + throw new ArgumentException("Discriminator element name name cannot be null or empty.", nameof(elementName)); } if (elementName.IndexOf('\0') != -1) { - throw new ArgumentException("Element names cannot contain nulls.", "elementName"); + throw new ArgumentException("Discriminator element name cannot contain nulls.", nameof(elementName)); } + _elementName = elementName; } diff --git a/tests/MongoDB.Bson.Tests/Serialization/Conventions/StandardDiscriminatorConventionTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Conventions/StandardDiscriminatorConventionTests.cs index f1d633bd724..25fabd6b479 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Conventions/StandardDiscriminatorConventionTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Conventions/StandardDiscriminatorConventionTests.cs @@ -31,7 +31,7 @@ public void TestConstructorThrowsWhenElementNameContainsNulls() [Fact] public void TestConstructorThrowsWhenElementNameIsNull() { - Assert.Throws(() => new ScalarDiscriminatorConvention(null)); + Assert.Throws(() => new ScalarDiscriminatorConvention(null)); } [Fact] diff --git a/tests/MongoDB.Driver.Tests/Jira/CSharp4040Tests.cs b/tests/MongoDB.Driver.Tests/Jira/CSharp4040Tests.cs new file mode 100644 index 00000000000..7858491fd0a --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Jira/CSharp4040Tests.cs @@ -0,0 +1,47 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using Xunit; + +namespace MongoDB.Driver.Tests.Jira +{ + public class CSharp4040Tests + { + private class BaseDocument + { + [BsonId] public ObjectId Id { get; set; } = ObjectId.GenerateNewId(); + + [BsonElement("_t")] + public string Field1 { get; set; } + } + + private class DerivedDocument : BaseDocument {} + + [Fact] + public void BsonClassMapSerializer_serialization_when_using_field_with_same_element_name_as_discriminator_should_throw() + { + var obj = new DerivedDocument { Field1 = "field1" }; + + var recordedException = Record.Exception(() => obj.ToJson(typeof(BaseDocument))); + recordedException.Should().NotBeNull(); + recordedException.Should().BeOfType(); + recordedException.Message.Should().Be("The discriminator element name cannot be _t because it is already being used" + + " by the property Field1 of type MongoDB.Driver.Tests.Jira.CSharp4040Tests+DerivedDocument"); + } + } +} From 27fd9e5251ba25c11f27753b6ae13f1d1b634937 Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Thu, 5 Jun 2025 14:01:26 -0700 Subject: [PATCH 15/24] CSHARP-5560: CSOT: Refactor IOperationExecutor to use operation context (#1676) --- .editorconfig | 8 +- src/MongoDB.Driver/AggregateHelper.cs | 96 ++ src/MongoDB.Driver/IOperationExecutor.cs | 36 +- src/MongoDB.Driver/MongoClient.cs | 409 +++----- src/MongoDB.Driver/MongoCollectionImpl.cs | 872 +++++++----------- src/MongoDB.Driver/MongoDatabase.cs | 552 +++++------ src/MongoDB.Driver/OperationExecutor.cs | 108 ++- src/MongoDB.Driver/ReadOperationOptions.cs | 42 + src/MongoDB.Driver/ReadPreferenceResolver.cs | 42 - src/MongoDB.Driver/WriteOperationOptions.cs | 20 + .../MockOperationExecutor.cs | 59 +- .../MongoDB.Driver.Tests/MongoClientTests.cs | 11 +- ...oDatabasTests.cs => MongoDatabaseTests.cs} | 20 +- .../OperationExecutorTests.cs | 156 ++++ .../ReadOperationOptionsTests.cs | 70 ++ 15 files changed, 1249 insertions(+), 1252 deletions(-) create mode 100644 src/MongoDB.Driver/AggregateHelper.cs create mode 100644 src/MongoDB.Driver/ReadOperationOptions.cs delete mode 100644 src/MongoDB.Driver/ReadPreferenceResolver.cs create mode 100644 src/MongoDB.Driver/WriteOperationOptions.cs rename tests/MongoDB.Driver.Tests/{MongoDatabasTests.cs => MongoDatabaseTests.cs} (98%) create mode 100644 tests/MongoDB.Driver.Tests/OperationExecutorTests.cs create mode 100644 tests/MongoDB.Driver.Tests/ReadOperationOptionsTests.cs diff --git a/.editorconfig b/.editorconfig index 1ab98bee463..482cea58e85 100644 --- a/.editorconfig +++ b/.editorconfig @@ -47,10 +47,10 @@ dotnet_style_qualification_for_property = false:suggestion dotnet_style_qualification_for_method = false:suggestion dotnet_style_qualification_for_event = false:suggestion -# Types: use keywords instead of BCL types, and permit var only when the type is clear -csharp_style_var_for_built_in_types = false:suggestion -csharp_style_var_when_type_is_apparent = false:none -csharp_style_var_elsewhere = false:suggestion +# Types: use keywords instead of BCL types, and prefer var instead of the explicit type +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = true:suggestion dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion dotnet_style_predefined_type_for_member_access = true:suggestion diff --git a/src/MongoDB.Driver/AggregateHelper.cs b/src/MongoDB.Driver/AggregateHelper.cs new file mode 100644 index 00000000000..c61628b5330 --- /dev/null +++ b/src/MongoDB.Driver/AggregateHelper.cs @@ -0,0 +1,96 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Linq; +using MongoDB.Bson; + +namespace MongoDB.Driver +{ + internal static class AggregateHelper + { + public static RenderedPipelineDefinition RenderAggregatePipeline(PipelineDefinition pipeline, RenderArgs renderArgs, out bool isAggregateToCollection) + { + var renderedPipeline = pipeline.Render(renderArgs); + + var lastStage = renderedPipeline.Documents.LastOrDefault(); + var lastStageName = lastStage?.GetElement(0).Name; + isAggregateToCollection = lastStageName == "$out" || lastStageName == "$merge"; + + return renderedPipeline; + } + + public static CollectionNamespace GetOutCollection(BsonDocument outStage, DatabaseNamespace defaultDatabaseNamespace) + { + var stageName = outStage.GetElement(0).Name; + switch (stageName) + { + case "$out": + { + var outValue = outStage[0]; + DatabaseNamespace outputDatabaseNamespace; + string outputCollectionName; + if (outValue.IsString) + { + outputDatabaseNamespace = defaultDatabaseNamespace; + outputCollectionName = outValue.AsString; + } + else + { + outputDatabaseNamespace = new DatabaseNamespace(outValue["db"].AsString); + outputCollectionName = outValue["coll"].AsString; + } + return new CollectionNamespace(outputDatabaseNamespace, outputCollectionName); + } + case "$merge": + { + var mergeArguments = outStage[0]; + DatabaseNamespace outputDatabaseNamespace; + string outputCollectionName; + if (mergeArguments.IsString) + { + outputDatabaseNamespace = defaultDatabaseNamespace; + outputCollectionName = mergeArguments.AsString; + } + else + { + var into = mergeArguments.AsBsonDocument["into"]; + if (into.IsString) + { + outputDatabaseNamespace = defaultDatabaseNamespace; + outputCollectionName = into.AsString; + } + else + { + if (into.AsBsonDocument.Contains("db")) + { + outputDatabaseNamespace = new DatabaseNamespace(into["db"].AsString); + } + else + { + outputDatabaseNamespace = defaultDatabaseNamespace; + } + outputCollectionName = into["coll"].AsString; + } + } + return new CollectionNamespace(outputDatabaseNamespace, outputCollectionName); + } + default: + throw new ArgumentException($"Unexpected stage name: {stageName}."); + } + } + } +} + diff --git a/src/MongoDB.Driver/IOperationExecutor.cs b/src/MongoDB.Driver/IOperationExecutor.cs index 1af86d6ff62..b935561469f 100644 --- a/src/MongoDB.Driver/IOperationExecutor.cs +++ b/src/MongoDB.Driver/IOperationExecutor.cs @@ -16,20 +16,40 @@ using System; using System.Threading; using System.Threading.Tasks; -using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Operations; namespace MongoDB.Driver { - internal interface IOperationExecutor + internal interface IOperationExecutor : IDisposable { - TResult ExecuteReadOperation(IReadBinding binding, IReadOperation operation, CancellationToken cancellationToken); - Task ExecuteReadOperationAsync(IReadBinding binding, IReadOperation operation, CancellationToken cancellationToken); + TResult ExecuteReadOperation( + IClientSessionHandle session, + IReadOperation operation, + ReadOperationOptions options, + bool allowChannelPinning, + CancellationToken cancellationToken); - TResult ExecuteWriteOperation(IWriteBinding binding, IWriteOperation operation, CancellationToken cancellationToken); - Task ExecuteWriteOperationAsync(IWriteBinding binding, IWriteOperation operation, CancellationToken cancellationToken); + Task ExecuteReadOperationAsync( + IClientSessionHandle session, + IReadOperation operation, + ReadOperationOptions options, + bool allowChannelPinning, + CancellationToken cancellationToken); - IClientSessionHandle StartImplicitSession(CancellationToken cancellationToken); - Task StartImplicitSessionAsync(CancellationToken cancellationToken); + TResult ExecuteWriteOperation( + IClientSessionHandle session, + IWriteOperation operation, + WriteOperationOptions options, + bool allowChannelPinning, + CancellationToken cancellationToken); + + Task ExecuteWriteOperationAsync( + IClientSessionHandle session, + IWriteOperation operation, + WriteOperationOptions options, + bool allowChannelPinning, + CancellationToken cancellationToken); + + IClientSessionHandle StartImplicitSession(); } } diff --git a/src/MongoDB.Driver/MongoClient.cs b/src/MongoDB.Driver/MongoClient.cs index 0fa1b4eb8d2..55d91940952 100644 --- a/src/MongoDB.Driver/MongoClient.cs +++ b/src/MongoDB.Driver/MongoClient.cs @@ -23,7 +23,6 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; @@ -42,9 +41,12 @@ public sealed class MongoClient : IMongoClient private readonly IClusterInternal _cluster; #pragma warning restore CA2213 // Disposable fields should be disposed private readonly IAutoEncryptionLibMongoCryptController _libMongoCryptController; + private readonly Func _operationExecutorFactory; private readonly IOperationExecutor _operationExecutor; private readonly MongoClientSettings _settings; private readonly ILogger _logger; + private readonly ReadOperationOptions _readOperationOptions; + private readonly WriteOperationOptions _writeOperationOptions; // constructors /// @@ -60,23 +62,9 @@ public MongoClient() /// /// The settings. public MongoClient(MongoClientSettings settings) + : this(settings, client => new OperationExecutor(client)) { - _settings = Ensure.IsNotNull(settings, nameof(settings)).FrozenCopy(); - _logger = _settings.LoggingSettings?.CreateLogger(); - _cluster = _settings.ClusterSource.Get(_settings.ToClusterKey()); - _operationExecutor = new OperationExecutor(this); - if (settings.AutoEncryptionOptions != null) - { - _libMongoCryptController = - MongoClientSettings.Extensions.AutoEncryptionProvider.CreateAutoCryptClientController(this, settings.AutoEncryptionOptions); - - _settings.LoggingSettings?.CreateLogger()?.LogTrace( - StructuredLogTemplateProviders.TopologyId_Message_SharedLibraryVersion, - _cluster.ClusterId, - "CryptClient created. Configured shared library version: ", - _libMongoCryptController.CryptSharedLibraryVersion() ?? "None"); - } } /// @@ -97,10 +85,27 @@ public MongoClient(string connectionString) { } - internal MongoClient(IOperationExecutor operationExecutor, MongoClientSettings settings) - : this(settings) + internal MongoClient(MongoClientSettings settings, Func operationExecutorFactory) { - _operationExecutor = operationExecutor; + _settings = Ensure.IsNotNull(settings, nameof(settings)).FrozenCopy(); + _operationExecutorFactory = Ensure.IsNotNull(operationExecutorFactory, nameof(operationExecutorFactory)); + _logger = _settings.LoggingSettings?.CreateLogger(); + _cluster = _settings.ClusterSource.Get(_settings.ToClusterKey()); + _operationExecutor = _operationExecutorFactory(this); + _readOperationOptions = new(DefaultReadPreference: _settings.ReadPreference); + _writeOperationOptions = new(); + + if (settings.AutoEncryptionOptions != null) + { + _libMongoCryptController = + MongoClientSettings.Extensions.AutoEncryptionProvider.CreateAutoCryptClientController(this, settings.AutoEncryptionOptions); + + _settings.LoggingSettings?.CreateLogger()?.LogTrace( + StructuredLogTemplateProviders.TopologyId_Message_SharedLibraryVersion, + _cluster.ClusterId, + "CryptClient created. Configured shared library version: ", + _libMongoCryptController.CryptSharedLibraryVersion() ?? "None"); + } } // public properties @@ -112,13 +117,11 @@ internal MongoClient(IOperationExecutor operationExecutor, MongoClientSettings s // internal properties internal IAutoEncryptionLibMongoCryptController LibMongoCryptController => ThrowIfDisposed(_libMongoCryptController); - internal IOperationExecutor OperationExecutor => ThrowIfDisposed(_operationExecutor); // internal methods internal void ConfigureAutoEncryptionMessageEncoderSettings(MessageEncoderSettings messageEncoderSettings) { ThrowIfDisposed(); - var autoEncryptionOptions = _settings.AutoEncryptionOptions; if (autoEncryptionOptions != null) { @@ -133,32 +136,36 @@ internal void ConfigureAutoEncryptionMessageEncoderSettings(MessageEncoderSettin // public methods /// public ClientBulkWriteResult BulkWrite(IReadOnlyList models, ClientBulkWriteOptions options = null, CancellationToken cancellationToken = default) - => UsingImplicitSession(session => BulkWrite(session, models, options, cancellationToken), cancellationToken); + { + ThrowIfDisposed(); + using var session = _operationExecutor.StartImplicitSession(); + return BulkWrite(session, models, options, cancellationToken); + } /// public ClientBulkWriteResult BulkWrite(IClientSessionHandle session, IReadOnlyList models, ClientBulkWriteOptions options = null, CancellationToken cancellationToken = default) { + Ensure.IsNotNull(session, nameof(session)); + ThrowIfDisposed(); var operation = CreateClientBulkWriteOperation(models, options); return ExecuteWriteOperation(session, operation, cancellationToken); } /// - public Task BulkWriteAsync(IReadOnlyList models, ClientBulkWriteOptions options = null, CancellationToken cancellationToken = default) - => UsingImplicitSession(session => BulkWriteAsync(session, models, options, cancellationToken), cancellationToken); - - /// - public Task BulkWriteAsync(IClientSessionHandle session, IReadOnlyList models, ClientBulkWriteOptions options = null, CancellationToken cancellationToken = default) + public async Task BulkWriteAsync(IReadOnlyList models, ClientBulkWriteOptions options = null, CancellationToken cancellationToken = default) { - var operation = CreateClientBulkWriteOperation(models, options); - return ExecuteWriteOperationAsync(session, operation, cancellationToken); + ThrowIfDisposed(); + using var session = _operationExecutor.StartImplicitSession(); + return await BulkWriteAsync(session, models, options, cancellationToken).ConfigureAwait(false); } /// - public void DropDatabase(string name, CancellationToken cancellationToken = default(CancellationToken)) + public Task BulkWriteAsync(IClientSessionHandle session, IReadOnlyList models, ClientBulkWriteOptions options = null, CancellationToken cancellationToken = default) { + Ensure.IsNotNull(session, nameof(session)); ThrowIfDisposed(); - - UsingImplicitSession(session => DropDatabase(session, name, cancellationToken), cancellationToken); + var operation = CreateClientBulkWriteOperation(models, options); + return ExecuteWriteOperationAsync(session, operation, cancellationToken); } /// @@ -180,6 +187,7 @@ public void Dispose(bool disposing) { _logger?.LogDebug(_cluster.ClusterId, "MongoClient disposing"); + _operationExecutor.Dispose(); _settings.ClusterSource.Return(_cluster); _libMongoCryptController?.Dispose(); @@ -191,39 +199,37 @@ public void Dispose(bool disposing) } /// - public void DropDatabase(IClientSessionHandle session, string name, CancellationToken cancellationToken = default(CancellationToken)) + public void DropDatabase(string name, CancellationToken cancellationToken = default) { - Ensure.IsNotNull(session, nameof(session)); ThrowIfDisposed(); - - var messageEncoderSettings = GetMessageEncoderSettings(); - var operation = new DropDatabaseOperation(new DatabaseNamespace(name), messageEncoderSettings) - { - WriteConcern = _settings.WriteConcern - }; - ExecuteWriteOperation(session, operation, cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + DropDatabase(session, name, cancellationToken); } /// - public Task DropDatabaseAsync(string name, CancellationToken cancellationToken = default(CancellationToken)) + public void DropDatabase(IClientSessionHandle session, string name, CancellationToken cancellationToken = default) { + Ensure.IsNotNull(session, nameof(session)); ThrowIfDisposed(); - - return UsingImplicitSessionAsync(session => DropDatabaseAsync(session, name, cancellationToken), cancellationToken); + var operation = CreateDropDatabaseOperation(name); + ExecuteWriteOperation(session, operation, cancellationToken); } /// - public Task DropDatabaseAsync(IClientSessionHandle session, string name, CancellationToken cancellationToken = default(CancellationToken)) + public async Task DropDatabaseAsync(string name, CancellationToken cancellationToken = default) { ThrowIfDisposed(); + using var session = _operationExecutor.StartImplicitSession(); + await DropDatabaseAsync(session, name, cancellationToken).ConfigureAwait(false); + } + /// + public Task DropDatabaseAsync(IClientSessionHandle session, string name, CancellationToken cancellationToken = default) + { Ensure.IsNotNull(session, nameof(session)); - var messageEncoderSettings = GetMessageEncoderSettings(); - var operation = new DropDatabaseOperation(new DatabaseNamespace(name), messageEncoderSettings) - { - WriteConcern = _settings.WriteConcern - }; - return ExecuteWriteOperationAsync(session, operation, cancellationToken); + ThrowIfDisposed(); + var opertion = CreateDropDatabaseOperation(name); + return ExecuteWriteOperationAsync(session, opertion, cancellationToken); } /// @@ -241,205 +247,143 @@ public IMongoDatabase GetDatabase(string name, MongoDatabaseSettings settings = } /// - public IAsyncCursor ListDatabaseNames( - CancellationToken cancellationToken = default(CancellationToken)) - { - ThrowIfDisposed(); - - return ListDatabaseNames(options: null, cancellationToken); - } + public IAsyncCursor ListDatabaseNames(CancellationToken cancellationToken = default) + => ListDatabaseNames(options: null, cancellationToken); /// public IAsyncCursor ListDatabaseNames( ListDatabaseNamesOptions options, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { ThrowIfDisposed(); - - return UsingImplicitSession(session => ListDatabaseNames(session, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return ListDatabaseNames(session, options, cancellationToken); } /// public IAsyncCursor ListDatabaseNames( IClientSessionHandle session, - CancellationToken cancellationToken = default(CancellationToken)) - { - ThrowIfDisposed(); - - return ListDatabaseNames(session, options: null, cancellationToken); - } + CancellationToken cancellationToken = default) + => ListDatabaseNames(session, options: null, cancellationToken); /// public IAsyncCursor ListDatabaseNames( IClientSessionHandle session, ListDatabaseNamesOptions options, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { ThrowIfDisposed(); - var listDatabasesOptions = CreateListDatabasesOptionsFromListDatabaseNamesOptions(options); var databases = ListDatabases(session, listDatabasesOptions, cancellationToken); - return CreateDatabaseNamesCursor(databases); } /// - public Task> ListDatabaseNamesAsync( - CancellationToken cancellationToken = default(CancellationToken)) - { - ThrowIfDisposed(); - - return ListDatabaseNamesAsync(options: null, cancellationToken); - } + public Task> ListDatabaseNamesAsync(CancellationToken cancellationToken = default) + => ListDatabaseNamesAsync(options: null, cancellationToken); /// - public Task> ListDatabaseNamesAsync( + public async Task> ListDatabaseNamesAsync( ListDatabaseNamesOptions options, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { ThrowIfDisposed(); - - return UsingImplicitSessionAsync(session => ListDatabaseNamesAsync(session, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await ListDatabaseNamesAsync(session, options, cancellationToken).ConfigureAwait(false); } /// public Task> ListDatabaseNamesAsync( IClientSessionHandle session, - CancellationToken cancellationToken = default(CancellationToken)) - { - ThrowIfDisposed(); - - return ListDatabaseNamesAsync(session, options: null, cancellationToken); - } + CancellationToken cancellationToken = default) + => ListDatabaseNamesAsync(session, options: null, cancellationToken); /// public async Task> ListDatabaseNamesAsync( IClientSessionHandle session, ListDatabaseNamesOptions options, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { ThrowIfDisposed(); - var listDatabasesOptions = CreateListDatabasesOptionsFromListDatabaseNamesOptions(options); var databases = await ListDatabasesAsync(session, listDatabasesOptions, cancellationToken).ConfigureAwait(false); - return CreateDatabaseNamesCursor(databases); } /// - public IAsyncCursor ListDatabases( - CancellationToken cancellationToken = default(CancellationToken)) + public IAsyncCursor ListDatabases(CancellationToken cancellationToken) { ThrowIfDisposed(); - - return UsingImplicitSession(session => ListDatabases(session, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return ListDatabases(session, cancellationToken); } /// public IAsyncCursor ListDatabases( ListDatabasesOptions options, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { ThrowIfDisposed(); - - return UsingImplicitSession(session => ListDatabases(session, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return ListDatabases(session, options, cancellationToken); } /// public IAsyncCursor ListDatabases( IClientSessionHandle session, - CancellationToken cancellationToken = default(CancellationToken)) - { - ThrowIfDisposed(); - - return ListDatabases(session, null, cancellationToken); - } + CancellationToken cancellationToken = default) + => ListDatabases(session, null, cancellationToken); /// public IAsyncCursor ListDatabases( IClientSessionHandle session, ListDatabasesOptions options, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { ThrowIfDisposed(); - Ensure.IsNotNull(session, nameof(session)); - options = options ?? new ListDatabasesOptions(); - var messageEncoderSettings = GetMessageEncoderSettings(); - var translationOptions = _settings.TranslationOptions; - var operation = CreateListDatabaseOperation(options, messageEncoderSettings, translationOptions); + var operation = CreateListDatabasesOperation(options); return ExecuteReadOperation(session, operation, cancellationToken); } /// - public Task> ListDatabasesAsync( - CancellationToken cancellationToken = default(CancellationToken)) + public async Task> ListDatabasesAsync(CancellationToken cancellationToken = default) { ThrowIfDisposed(); - - return UsingImplicitSessionAsync(session => ListDatabasesAsync(session, null, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await ListDatabasesAsync(session, cancellationToken).ConfigureAwait(false); } /// - public Task> ListDatabasesAsync( + public async Task> ListDatabasesAsync( ListDatabasesOptions options, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { ThrowIfDisposed(); - - return UsingImplicitSessionAsync(session => ListDatabasesAsync(session, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await ListDatabasesAsync(session, options, cancellationToken).ConfigureAwait(false); } /// public Task> ListDatabasesAsync( IClientSessionHandle session, - CancellationToken cancellationToken = default(CancellationToken)) - { - ThrowIfDisposed(); - - return ListDatabasesAsync(session, null, cancellationToken); - } + CancellationToken cancellationToken = default) + => ListDatabasesAsync(session, null, cancellationToken); /// public Task> ListDatabasesAsync( IClientSessionHandle session, ListDatabasesOptions options, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); ThrowIfDisposed(); - - options = options ?? new ListDatabasesOptions(); - var messageEncoderSettings = GetMessageEncoderSettings(); - var translationOptions = _settings.TranslationOptions; - var operation = CreateListDatabaseOperation(options, messageEncoderSettings, translationOptions); + var operation = CreateListDatabasesOperation(options); return ExecuteReadOperationAsync(session, operation, cancellationToken); } - /// - /// Starts an implicit session. - /// - /// A session. - internal IClientSessionHandle StartImplicitSession(CancellationToken cancellationToken) - { - ThrowIfDisposed(); - - return StartImplicitSession(); - } - - /// - /// Starts an implicit session. - /// - /// A Task whose result is a session. - internal Task StartImplicitSessionAsync(CancellationToken cancellationToken) - { - ThrowIfDisposed(); - - return Task.FromResult(StartImplicitSession()); - } - /// - public IClientSessionHandle StartSession(ClientSessionOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + public IClientSessionHandle StartSession(ClientSessionOptions options = null, CancellationToken cancellationToken = default) { ThrowIfDisposed(); @@ -447,7 +391,7 @@ internal Task StartImplicitSessionAsync(CancellationToken } /// - public Task StartSessionAsync(ClientSessionOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + public Task StartSessionAsync(ClientSessionOptions options = null, CancellationToken cancellationToken = default) { ThrowIfDisposed(); @@ -458,11 +402,11 @@ internal Task StartImplicitSessionAsync(CancellationToken public IChangeStreamCursor Watch( PipelineDefinition, TResult> pipeline, ChangeStreamOptions options = null, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { ThrowIfDisposed(); - - return UsingImplicitSession(session => Watch(session, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return Watch(session, pipeline, options, cancellationToken); } /// @@ -470,26 +414,24 @@ public IChangeStreamCursor Watch( IClientSessionHandle session, PipelineDefinition, TResult> pipeline, ChangeStreamOptions options = null, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(pipeline, nameof(pipeline)); ThrowIfDisposed(); - - var translationOptions = _settings.TranslationOptions; - var operation = CreateChangeStreamOperation(pipeline, options, translationOptions); + var operation = CreateChangeStreamOperation(pipeline, options); return ExecuteReadOperation(session, operation, cancellationToken); } /// - public Task> WatchAsync( + public async Task> WatchAsync( PipelineDefinition, TResult> pipeline, ChangeStreamOptions options = null, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { ThrowIfDisposed(); - - return UsingImplicitSessionAsync(session => WatchAsync(session, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await WatchAsync(session, pipeline, options, cancellationToken).ConfigureAwait(false); } /// @@ -497,15 +439,12 @@ public Task> WatchAsync( IClientSessionHandle session, PipelineDefinition, TResult> pipeline, ChangeStreamOptions options = null, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(pipeline, nameof(pipeline)); - ThrowIfDisposed(); - - var translationOptions = _settings.TranslationOptions; - var operation = CreateChangeStreamOperation(pipeline, options, translationOptions); + var operation = CreateChangeStreamOperation(pipeline, options); return ExecuteReadOperationAsync(session, operation, cancellationToken); } @@ -513,40 +452,37 @@ public Task> WatchAsync( public IMongoClient WithReadConcern(ReadConcern readConcern) { Ensure.IsNotNull(readConcern, nameof(readConcern)); - ThrowIfDisposed(); var newSettings = Settings.Clone(); newSettings.ReadConcern = readConcern; - return new MongoClient(_operationExecutor, newSettings); + return new MongoClient(newSettings, _operationExecutorFactory); } /// public IMongoClient WithReadPreference(ReadPreference readPreference) { Ensure.IsNotNull(readPreference, nameof(readPreference)); - ThrowIfDisposed(); var newSettings = Settings.Clone(); newSettings.ReadPreference = readPreference; - return new MongoClient(_operationExecutor, newSettings); + return new MongoClient(newSettings, _operationExecutorFactory); } /// public IMongoClient WithWriteConcern(WriteConcern writeConcern) { Ensure.IsNotNull(writeConcern, nameof(writeConcern)); - ThrowIfDisposed(); var newSettings = Settings.Clone(); newSettings.WriteConcern = writeConcern; - return new MongoClient(_operationExecutor, newSettings); + return new MongoClient(newSettings, _operationExecutorFactory); } // private methods - private ClientBulkWriteOperation CreateClientBulkWriteOperation(IReadOnlyList models, ClientBulkWriteOptions options = null) + private ClientBulkWriteOperation CreateClientBulkWriteOperation(IReadOnlyList models, ClientBulkWriteOptions options) { if (_settings.AutoEncryptionOptions != null) { @@ -578,17 +514,22 @@ private ClientBulkWriteOperation CreateClientBulkWriteOperation(IReadOnlyList CreateDatabaseNamesCursor(IAsyncCursor cursor) - { - return new BatchTransformingAsyncCursor( + => new BatchTransformingAsyncCursor( cursor, databases => databases.Select(database => database["name"].AsString)); - } - private ListDatabasesOperation CreateListDatabaseOperation( - ListDatabasesOptions options, - MessageEncoderSettings messageEncoderSettings, - ExpressionTranslationOptions translationOptions) + private DropDatabaseOperation CreateDropDatabaseOperation(string name) + => new(new DatabaseNamespace(name), GetMessageEncoderSettings()) + { + WriteConcern = _settings.WriteConcern + }; + + private ListDatabasesOperation CreateListDatabasesOperation(ListDatabasesOptions options) { + options ??= new ListDatabasesOptions(); + var messageEncoderSettings = GetMessageEncoderSettings(); + var translationOptions = _settings.TranslationOptions; + return new ListDatabasesOperation(messageEncoderSettings) { AuthorizedDatabases = options.AuthorizedDatabases, @@ -612,69 +553,28 @@ private ListDatabasesOptions CreateListDatabasesOptionsFromListDatabaseNamesOpti return listDatabasesOptions; } - private IReadBindingHandle CreateReadBinding(IClientSessionHandle session) - { - var readPreference = _settings.ReadPreference; - if (session.IsInTransaction && readPreference.ReadPreferenceMode != ReadPreferenceMode.Primary) - { - throw new InvalidOperationException("Read preference in a transaction must be primary."); - } - - var binding = new ReadPreferenceBinding(_cluster, readPreference, session.WrappedCoreSession.Fork()); - return new ReadBindingHandle(binding); - } - - private IReadWriteBindingHandle CreateReadWriteBinding(IClientSessionHandle session) - { - var binding = new WritableServerBinding(_cluster, session.WrappedCoreSession.Fork()); - return new ReadWriteBindingHandle(binding); - } - private ChangeStreamOperation CreateChangeStreamOperation( PipelineDefinition, TResult> pipeline, - ChangeStreamOptions options, - ExpressionTranslationOptions translationOptions) - { - return ChangeStreamHelper.CreateChangeStreamOperation( + ChangeStreamOptions options) + => ChangeStreamHelper.CreateChangeStreamOperation( pipeline, options, _settings.ReadConcern, GetMessageEncoderSettings(), _settings.RetryReads, - translationOptions); - } + _settings.TranslationOptions); - private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var binding = CreateReadBinding(session)) - { - return _operationExecutor.ExecuteReadOperation(binding, operation, cancellationToken); - } - } + private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, CancellationToken cancellationToken) + => _operationExecutor.ExecuteReadOperation(session, operation, _readOperationOptions, false, cancellationToken); - private async Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var binding = CreateReadBinding(session)) - { - return await _operationExecutor.ExecuteReadOperationAsync(binding, operation, cancellationToken).ConfigureAwait(false); - } - } + private Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, CancellationToken cancellationToken) + => _operationExecutor.ExecuteReadOperationAsync(session, operation, _readOperationOptions, false, cancellationToken); - private TResult ExecuteWriteOperation(IClientSessionHandle session, IWriteOperation operation, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var binding = CreateReadWriteBinding(session)) - { - return _operationExecutor.ExecuteWriteOperation(binding, operation, cancellationToken); - } - } + private TResult ExecuteWriteOperation(IClientSessionHandle session, IWriteOperation operation, CancellationToken cancellationToken) + => _operationExecutor.ExecuteWriteOperation(session, operation, _writeOperationOptions, false, cancellationToken); - private async Task ExecuteWriteOperationAsync(IClientSessionHandle session, IWriteOperation operation, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var binding = CreateReadWriteBinding(session)) - { - return await _operationExecutor.ExecuteWriteOperationAsync(binding, operation, cancellationToken).ConfigureAwait(false); - } - } + private Task ExecuteWriteOperationAsync(IClientSessionHandle session, IWriteOperation operation, CancellationToken cancellationToken) + => _operationExecutor.ExecuteWriteOperationAsync(session, operation, _writeOperationOptions, false, cancellationToken); private MessageEncoderSettings GetMessageEncoderSettings() { @@ -696,13 +596,6 @@ private RenderArgs GetRenderArgs() return new RenderArgs(BsonDocumentSerializer.Instance, serializerRegistry, translationOptions: translationOptions); } - private IClientSessionHandle StartImplicitSession() - { - var options = new ClientSessionOptions { CausalConsistency = false, Snapshot = false }; - ICoreSessionHandle coreSession = _cluster.StartSession(options.ToCore(isImplicit: true)); - return new ClientSessionHandle(this, options, coreSession); - } - private IClientSessionHandle StartSession(ClientSessionOptions options) { if (options != null && options.Snapshot && options.CausalConsistency == true) @@ -718,37 +611,5 @@ private IClientSessionHandle StartSession(ClientSessionOptions options) private void ThrowIfDisposed() => ThrowIfDisposed(string.Empty); private T ThrowIfDisposed(T value) => _disposed ? throw new ObjectDisposedException(GetType().Name) : value; - - private void UsingImplicitSession(Action func, CancellationToken cancellationToken) - { - using (var session = StartImplicitSession(cancellationToken)) - { - func(session); - } - } - - private TResult UsingImplicitSession(Func func, CancellationToken cancellationToken) - { - using (var session = StartImplicitSession(cancellationToken)) - { - return func(session); - } - } - - private async Task UsingImplicitSessionAsync(Func funcAsync, CancellationToken cancellationToken) - { - using (var session = await StartImplicitSessionAsync(cancellationToken).ConfigureAwait(false)) - { - await funcAsync(session).ConfigureAwait(false); - } - } - - private async Task UsingImplicitSessionAsync(Func> funcAsync, CancellationToken cancellationToken) - { - using (var session = await StartImplicitSessionAsync(cancellationToken).ConfigureAwait(false)) - { - return await funcAsync(session).ConfigureAwait(false); - } - } } } diff --git a/src/MongoDB.Driver/MongoCollectionImpl.cs b/src/MongoDB.Driver/MongoCollectionImpl.cs index 51f03dc0a04..4b0b144e960 100644 --- a/src/MongoDB.Driver/MongoCollectionImpl.cs +++ b/src/MongoDB.Driver/MongoCollectionImpl.cs @@ -21,8 +21,6 @@ using MongoDB.Bson; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; -using MongoDB.Driver.Core; -using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Operations; @@ -41,6 +39,8 @@ internal sealed class MongoCollectionImpl : MongoCollectionBase _documentSerializer; private readonly MongoCollectionSettings _settings; + private readonly ReadOperationOptions _readOperationOptions; + private readonly WriteOperationOptions _writeOperationOptions; // constructors public MongoCollectionImpl(IMongoDatabase database, CollectionNamespace collectionNamespace, MongoCollectionSettings settings, IClusterInternal cluster, IOperationExecutor operationExecutor) @@ -58,6 +58,8 @@ private MongoCollectionImpl(IMongoDatabase database, CollectionNamespace collect _documentSerializer = Ensure.IsNotNull(documentSerializer, nameof(documentSerializer)); _messageEncoderSettings = GetMessageEncoderSettings(); + _readOperationOptions = new(DefaultReadPreference: _settings.ReadPreference); + _writeOperationOptions = new(); } // properties @@ -94,36 +96,25 @@ public override MongoCollectionSettings Settings } // public methods - public override IAsyncCursor Aggregate(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override IAsyncCursor Aggregate(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => Aggregate(session, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return Aggregate(session, pipeline, options, cancellationToken: cancellationToken); } - public override IAsyncCursor Aggregate(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override IAsyncCursor Aggregate(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(pipeline, nameof(pipeline)); - options = options ?? new AggregateOptions(); + options ??= new AggregateOptions(); var renderArgs = GetRenderArgs(options.TranslationOptions); - var renderedPipeline = pipeline.Render(renderArgs); - - var lastStage = renderedPipeline.Documents.LastOrDefault(); - var lastStageName = lastStage?.GetElement(0).Name; - if (lastStage != null && (lastStageName == "$out" || lastStageName == "$merge")) + var renderedPipeline = AggregateHelper.RenderAggregatePipeline(pipeline, renderArgs, out var isAggregateToCollection); + if (isAggregateToCollection) { var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); ExecuteWriteOperation(session, aggregateOperation, cancellationToken); - - // we want to delay execution of the find because the user may - // not want to iterate the results at all... - var findOperation = CreateAggregateToCollectionFindOperation(lastStage, renderedPipeline.OutputSerializer, options); - var forkedSession = session.Fork(); - var deferredCursor = new DeferredAsyncCursor( - () => forkedSession.Dispose(), - ct => ExecuteReadOperation(forkedSession, findOperation, ReadPreference.Primary, ct), - ct => ExecuteReadOperationAsync(forkedSession, findOperation, ReadPreference.Primary, ct)); - return deferredCursor; + return CreateAggregateToCollectionResultCursor(session, renderedPipeline, options); } else { @@ -132,36 +123,25 @@ public override MongoCollectionSettings Settings } } - public override Task> AggregateAsync(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task> AggregateAsync(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => AggregateAsync(session, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await AggregateAsync(session, pipeline, options, cancellationToken).ConfigureAwait(false); } - public override async Task> AggregateAsync(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task> AggregateAsync(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(pipeline, nameof(pipeline)); - options = options ?? new AggregateOptions(); + options ??= new AggregateOptions(); var renderArgs = GetRenderArgs(options.TranslationOptions); - var renderedPipeline = pipeline.Render(renderArgs); - - var lastStage = renderedPipeline.Documents.LastOrDefault(); - var lastStageName = lastStage?.GetElement(0).Name; - if (lastStage != null && (lastStageName == "$out" || lastStageName == "$merge")) + var renderedPipeline = AggregateHelper.RenderAggregatePipeline(pipeline, renderArgs, out var isAggregateToCollection); + if (isAggregateToCollection) { var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); await ExecuteWriteOperationAsync(session, aggregateOperation, cancellationToken).ConfigureAwait(false); - - // we want to delay execution of the find because the user may - // not want to iterate the results at all... - var findOperation = CreateAggregateToCollectionFindOperation(lastStage, renderedPipeline.OutputSerializer, options); - var forkedSession = session.Fork(); - var deferredCursor = new DeferredAsyncCursor( - () => forkedSession.Dispose(), - ct => ExecuteReadOperation(forkedSession, findOperation, ReadPreference.Primary, ct), - ct => ExecuteReadOperationAsync(forkedSession, findOperation, ReadPreference.Primary, ct)); - return await Task.FromResult>(deferredCursor).ConfigureAwait(false); + return CreateAggregateToCollectionResultCursor(session, renderedPipeline, options); } else { @@ -170,23 +150,21 @@ public override MongoCollectionSettings Settings } } - public override void AggregateToCollection(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override void AggregateToCollection(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { - UsingImplicitSession(session => AggregateToCollection(session, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + AggregateToCollection(session, pipeline, options, cancellationToken); } - public override void AggregateToCollection(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override void AggregateToCollection(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(pipeline, nameof(pipeline)); - options = options ?? new AggregateOptions(); + options ??= new AggregateOptions(); var renderArgs = GetRenderArgs(options.TranslationOptions); - var renderedPipeline = pipeline.Render(renderArgs); - - var lastStage = renderedPipeline.Documents.LastOrDefault(); - var lastStageName = lastStage?.GetElement(0).Name; - if (lastStage == null || (lastStageName != "$out" && lastStageName != "$merge")) + var renderedPipeline = AggregateHelper.RenderAggregatePipeline(pipeline, renderArgs, out var isAggregateToCollection); + if (renderedPipeline.Documents.Count == 0 || !isAggregateToCollection) { throw new InvalidOperationException("AggregateToCollection requires that the last stage be $out or $merge."); } @@ -195,56 +173,46 @@ public override MongoCollectionSettings Settings ExecuteWriteOperation(session, aggregateOperation, cancellationToken); } - public override Task AggregateToCollectionAsync(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task AggregateToCollectionAsync(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => AggregateToCollectionAsync(session, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + await AggregateToCollectionAsync(session, pipeline, options, cancellationToken).ConfigureAwait(false); } - public override async Task AggregateToCollectionAsync(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override Task AggregateToCollectionAsync(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(pipeline, nameof(pipeline)); - options = options ?? new AggregateOptions(); + options ??= new AggregateOptions(); var renderArgs = GetRenderArgs(options.TranslationOptions); - var renderedPipeline = pipeline.Render(renderArgs); - - var lastStage = renderedPipeline.Documents.LastOrDefault(); - var lastStageName = lastStage?.GetElement(0).Name; - if (lastStage == null || (lastStageName != "$out" && lastStageName != "$merge")) + var renderedPipeline = AggregateHelper.RenderAggregatePipeline(pipeline, renderArgs, out var isAggregateToCollection); + if (renderedPipeline.Documents.Count == 0 || !isAggregateToCollection) { throw new InvalidOperationException("AggregateToCollectionAsync requires that the last stage be $out or $merge."); } var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); - await ExecuteWriteOperationAsync(session, aggregateOperation, cancellationToken).ConfigureAwait(false); + return ExecuteWriteOperationAsync(session, aggregateOperation, cancellationToken); } - public override BulkWriteResult BulkWrite(IEnumerable> requests, BulkWriteOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override BulkWriteResult BulkWrite(IEnumerable> requests, BulkWriteOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => BulkWrite(session, requests, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return BulkWrite(session, requests, options, cancellationToken); } - public override BulkWriteResult BulkWrite(IClientSessionHandle session, IEnumerable> requests, BulkWriteOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override BulkWriteResult BulkWrite(IClientSessionHandle session, IEnumerable> requests, BulkWriteOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); - Ensure.IsNotNull((object)requests, nameof(requests)); - + Ensure.IsNotNull(requests, nameof(requests)); var requestsArray = requests.ToArray(); if (requestsArray.Length == 0) { throw new ArgumentException("Must contain at least 1 request.", nameof(requests)); } - foreach (var request in requestsArray) - { - request.ThrowIfNotValid(); - } - - options = options ?? new BulkWriteOptions(); - - var renderArgs = GetRenderArgs(); - var operation = CreateBulkWriteOperation(session, requestsArray, options, renderArgs); + var operation = CreateBulkWriteOperation(session, requestsArray, options); try { var result = ExecuteWriteOperation(session, operation, cancellationToken); @@ -256,31 +224,23 @@ public override MongoCollectionSettings Settings } } - public override Task> BulkWriteAsync(IEnumerable> requests, BulkWriteOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task> BulkWriteAsync(IEnumerable> requests, BulkWriteOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => BulkWriteAsync(session, requests, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await BulkWriteAsync(session, requests, options, cancellationToken).ConfigureAwait(false); } - public override async Task> BulkWriteAsync(IClientSessionHandle session, IEnumerable> requests, BulkWriteOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task> BulkWriteAsync(IClientSessionHandle session, IEnumerable> requests, BulkWriteOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); - Ensure.IsNotNull((object)requests, nameof(requests)); - + Ensure.IsNotNull(requests, nameof(requests)); var requestsArray = requests.ToArray(); if (requestsArray.Length == 0) { throw new ArgumentException("Must contain at least 1 request.", nameof(requests)); } - foreach (var request in requestsArray) - { - request.ThrowIfNotValid(); - } - - options = options ?? new BulkWriteOptions(); - - var renderArgs = GetRenderArgs(); - var operation = CreateBulkWriteOperation(session, requestsArray, options, renderArgs); + var operation = CreateBulkWriteOperation(session, requestsArray, options); try { var result = await ExecuteWriteOperationAsync(session, operation, cancellationToken).ConfigureAwait(false); @@ -293,314 +253,296 @@ public override MongoCollectionSettings Settings } [Obsolete("Use CountDocuments or EstimatedDocumentCount instead.")] - public override long Count(FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override long Count(FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => Count(session, filter, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return Count(session, filter, options, cancellationToken); } [Obsolete("Use CountDocuments or EstimatedDocumentCount instead.")] - public override long Count(IClientSessionHandle session, FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override long Count(IClientSessionHandle session, FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(filter, nameof(filter)); - options = options ?? new CountOptions(); - var renderArgs = GetRenderArgs(); - var operation = CreateCountOperation(filter, options, renderArgs); + var operation = CreateCountOperation(filter, options); return ExecuteReadOperation(session, operation, cancellationToken); } [Obsolete("Use CountDocumentsAsync or EstimatedDocumentCountAsync instead.")] - public override Task CountAsync(FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task CountAsync(FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => CountAsync(session, filter, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await CountAsync(session, filter, options, cancellationToken).ConfigureAwait(false); } [Obsolete("Use CountDocumentsAsync or EstimatedDocumentCountAsync instead.")] - public override Task CountAsync(IClientSessionHandle session, FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override Task CountAsync(IClientSessionHandle session, FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(filter, nameof(filter)); - options = options ?? new CountOptions(); - var renderArgs = GetRenderArgs(); - var operation = CreateCountOperation(filter, options, renderArgs); + var operation = CreateCountOperation(filter, options); return ExecuteReadOperationAsync(session, operation, cancellationToken); } - public override long CountDocuments(FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override long CountDocuments(FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => CountDocuments(session, filter, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return CountDocuments(session, filter, options, cancellationToken); } - public override long CountDocuments(IClientSessionHandle session, FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override long CountDocuments(IClientSessionHandle session, FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(filter, nameof(filter)); - options = options ?? new CountOptions(); - var renderArgs = GetRenderArgs(); - var operation = CreateCountDocumentsOperation(filter, options, renderArgs); + var operation = CreateCountDocumentsOperation(filter, options); return ExecuteReadOperation(session, operation, cancellationToken); } - public override Task CountDocumentsAsync(FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task CountDocumentsAsync(FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => CountDocumentsAsync(session, filter, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await CountDocumentsAsync(session, filter, options, cancellationToken).ConfigureAwait(false); } - public override Task CountDocumentsAsync(IClientSessionHandle session, FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override Task CountDocumentsAsync(IClientSessionHandle session, FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(filter, nameof(filter)); - options = options ?? new CountOptions(); - var renderArgs = GetRenderArgs(); - var operation = CreateCountDocumentsOperation(filter, options, renderArgs); + var operation = CreateCountDocumentsOperation(filter, options); return ExecuteReadOperationAsync(session, operation, cancellationToken); } - public override IAsyncCursor Distinct(FieldDefinition field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override IAsyncCursor Distinct(FieldDefinition field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => Distinct(session, field, filter, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return Distinct(session, field, filter, options, cancellationToken); } - public override IAsyncCursor Distinct(IClientSessionHandle session, FieldDefinition field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override IAsyncCursor Distinct(IClientSessionHandle session, FieldDefinition field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(field, nameof(field)); Ensure.IsNotNull(filter, nameof(filter)); - options = options ?? new DistinctOptions(); - var renderArgs = GetRenderArgs(); - var operation = CreateDistinctOperation(field, filter, options, renderArgs); + var operation = CreateDistinctOperation(field, filter, options); return ExecuteReadOperation(session, operation, cancellationToken); } - public override Task> DistinctAsync(FieldDefinition field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task> DistinctAsync(FieldDefinition field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => DistinctAsync(session, field, filter, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await DistinctAsync(session, field, filter, options, cancellationToken).ConfigureAwait(false); } - public override Task> DistinctAsync(IClientSessionHandle session, FieldDefinition field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override Task> DistinctAsync(IClientSessionHandle session, FieldDefinition field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(field, nameof(field)); Ensure.IsNotNull(filter, nameof(filter)); - options = options ?? new DistinctOptions(); - var renderArgs = GetRenderArgs(); - var operation = CreateDistinctOperation(field, filter, options, renderArgs); + var operation = CreateDistinctOperation(field, filter, options); return ExecuteReadOperationAsync(session, operation, cancellationToken); } - public override IAsyncCursor DistinctMany(FieldDefinition> field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override IAsyncCursor DistinctMany(FieldDefinition> field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => DistinctMany(session, field, filter, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return DistinctMany(session, field, filter, options, cancellationToken); } - public override IAsyncCursor DistinctMany(IClientSessionHandle session, FieldDefinition> field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override IAsyncCursor DistinctMany(IClientSessionHandle session, FieldDefinition> field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(field, nameof(field)); Ensure.IsNotNull(filter, nameof(filter)); - options = options ?? new DistinctOptions(); - var renderArgs = GetRenderArgs(); - var operation = CreateDistinctManyOperation(field, filter, options, renderArgs); + var operation = CreateDistinctManyOperation(field, filter, options); return ExecuteReadOperation(session, operation, cancellationToken); } - public override Task> DistinctManyAsync(FieldDefinition> field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task> DistinctManyAsync(FieldDefinition> field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => DistinctManyAsync(session, field, filter, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await DistinctManyAsync(session, field, filter, options, cancellationToken).ConfigureAwait(false); } - public override Task> DistinctManyAsync(IClientSessionHandle session, FieldDefinition> field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override Task> DistinctManyAsync(IClientSessionHandle session, FieldDefinition> field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(field, nameof(field)); Ensure.IsNotNull(filter, nameof(filter)); - options = options ?? new DistinctOptions(); - var renderArgs = GetRenderArgs(); - var operation = CreateDistinctManyOperation(field, filter, options, renderArgs); + var operation = CreateDistinctManyOperation(field, filter, options); return ExecuteReadOperationAsync(session, operation, cancellationToken); } - public override long EstimatedDocumentCount(EstimatedDocumentCountOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override long EstimatedDocumentCount(EstimatedDocumentCountOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => - { - var operation = CreateEstimatedDocumentCountOperation(options); - return ExecuteReadOperation(session, operation, cancellationToken); - }); + using var session = _operationExecutor.StartImplicitSession(); + var operation = CreateEstimatedDocumentCountOperation(options); + return ExecuteReadOperation(session, operation, cancellationToken); } - public override Task EstimatedDocumentCountAsync(EstimatedDocumentCountOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task EstimatedDocumentCountAsync(EstimatedDocumentCountOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => - { - var operation = CreateEstimatedDocumentCountOperation(options); - return ExecuteReadOperationAsync(session, operation, cancellationToken); - }); + using var session = _operationExecutor.StartImplicitSession(); + var operation = CreateEstimatedDocumentCountOperation(options); + return await ExecuteReadOperationAsync(session, operation, cancellationToken).ConfigureAwait(false); } - public override IAsyncCursor FindSync(FilterDefinition filter, FindOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override IAsyncCursor FindSync(FilterDefinition filter, FindOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => FindSync(session, filter, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return FindSync(session, filter, options, cancellationToken); } - public override IAsyncCursor FindSync(IClientSessionHandle session, FilterDefinition filter, FindOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override IAsyncCursor FindSync(IClientSessionHandle session, FilterDefinition filter, FindOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(filter, nameof(filter)); - options = options ?? new FindOptions(); - var renderArgs = GetRenderArgs(options.TranslationOptions); - var operation = CreateFindOperation(filter, options, renderArgs); + var operation = CreateFindOperation(filter, options); return ExecuteReadOperation(session, operation, cancellationToken); } - public override Task> FindAsync(FilterDefinition filter, FindOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task> FindAsync(FilterDefinition filter, FindOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => FindAsync(session, filter, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await FindAsync(session, filter, options, cancellationToken).ConfigureAwait(false); } - public override Task> FindAsync(IClientSessionHandle session, FilterDefinition filter, FindOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override Task> FindAsync(IClientSessionHandle session, FilterDefinition filter, FindOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(filter, nameof(filter)); - options = options ?? new FindOptions(); - var renderArgs = GetRenderArgs(options.TranslationOptions); - var operation = CreateFindOperation(filter, options, renderArgs); + var operation = CreateFindOperation(filter, options); return ExecuteReadOperationAsync(session, operation, cancellationToken); } - public override TProjection FindOneAndDelete(FilterDefinition filter, FindOneAndDeleteOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override TProjection FindOneAndDelete(FilterDefinition filter, FindOneAndDeleteOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => FindOneAndDelete(session, filter, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return FindOneAndDelete(session, filter, options, cancellationToken); } - public override TProjection FindOneAndDelete(IClientSessionHandle session, FilterDefinition filter, FindOneAndDeleteOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override TProjection FindOneAndDelete(IClientSessionHandle session, FilterDefinition filter, FindOneAndDeleteOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(filter, nameof(filter)); - options = options ?? new FindOneAndDeleteOptions(); - var renderArgs = GetRenderArgs(); - var operation = CreateFindOneAndDeleteOperation(filter, options, renderArgs); + var operation = CreateFindOneAndDeleteOperation(filter, options); return ExecuteWriteOperation(session, operation, cancellationToken); } - public override Task FindOneAndDeleteAsync(FilterDefinition filter, FindOneAndDeleteOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task FindOneAndDeleteAsync(FilterDefinition filter, FindOneAndDeleteOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => FindOneAndDeleteAsync(session, filter, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await FindOneAndDeleteAsync(session, filter, options, cancellationToken).ConfigureAwait(false); } - public override Task FindOneAndDeleteAsync(IClientSessionHandle session, FilterDefinition filter, FindOneAndDeleteOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override Task FindOneAndDeleteAsync(IClientSessionHandle session, FilterDefinition filter, FindOneAndDeleteOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(filter, nameof(filter)); - options = options ?? new FindOneAndDeleteOptions(); - var renderArgs = GetRenderArgs(); - var operation = CreateFindOneAndDeleteOperation(filter, options, renderArgs); + var operation = CreateFindOneAndDeleteOperation(filter, options); return ExecuteWriteOperationAsync(session, operation, cancellationToken); } - public override TProjection FindOneAndReplace(FilterDefinition filter, TDocument replacement, FindOneAndReplaceOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override TProjection FindOneAndReplace(FilterDefinition filter, TDocument replacement, FindOneAndReplaceOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => FindOneAndReplace(session, filter, replacement, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return FindOneAndReplace(session, filter, replacement, options, cancellationToken); } - public override TProjection FindOneAndReplace(IClientSessionHandle session, FilterDefinition filter, TDocument replacement, FindOneAndReplaceOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override TProjection FindOneAndReplace(IClientSessionHandle session, FilterDefinition filter, TDocument replacement, FindOneAndReplaceOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(filter, nameof(filter)); var replacementObject = Ensure.IsNotNull((object)replacement, nameof(replacement)); // only box once if it's a struct - options = options ?? new FindOneAndReplaceOptions(); - var renderArgs = GetRenderArgs(); - var operation = CreateFindOneAndReplaceOperation(filter, replacementObject, options, renderArgs); + var operation = CreateFindOneAndReplaceOperation(filter, replacementObject, options); return ExecuteWriteOperation(session, operation, cancellationToken); } - public override Task FindOneAndReplaceAsync(FilterDefinition filter, TDocument replacement, FindOneAndReplaceOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task FindOneAndReplaceAsync(FilterDefinition filter, TDocument replacement, FindOneAndReplaceOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => FindOneAndReplaceAsync(session, filter, replacement, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await FindOneAndReplaceAsync(session, filter, replacement, options, cancellationToken).ConfigureAwait(false); } - public override Task FindOneAndReplaceAsync(IClientSessionHandle session, FilterDefinition filter, TDocument replacement, FindOneAndReplaceOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override Task FindOneAndReplaceAsync(IClientSessionHandle session, FilterDefinition filter, TDocument replacement, FindOneAndReplaceOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(filter, nameof(filter)); var replacementObject = Ensure.IsNotNull((object)replacement, nameof(replacement)); // only box once if it's a struct - options = options ?? new FindOneAndReplaceOptions(); - var renderArgs = GetRenderArgs(); - var operation = CreateFindOneAndReplaceOperation(filter, replacementObject, options, renderArgs); + var operation = CreateFindOneAndReplaceOperation(filter, replacementObject, options); return ExecuteWriteOperationAsync(session, operation, cancellationToken); } - public override TProjection FindOneAndUpdate(FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override TProjection FindOneAndUpdate(FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => FindOneAndUpdate(session, filter, update, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return FindOneAndUpdate(session, filter, update, options, cancellationToken); } - public override TProjection FindOneAndUpdate(IClientSessionHandle session, FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override TProjection FindOneAndUpdate(IClientSessionHandle session, FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(filter, nameof(filter)); Ensure.IsNotNull(update, nameof(update)); - options = options ?? new FindOneAndUpdateOptions(); + options ??= new FindOneAndUpdateOptions(); if (update is PipelineUpdateDefinition && (options.ArrayFilters != null && options.ArrayFilters.Any())) { throw new NotSupportedException("An arrayfilter is not supported in the pipeline-style update."); } - var renderArgs = GetRenderArgs(); - var operation = CreateFindOneAndUpdateOperation(filter, update, options, renderArgs); + var operation = CreateFindOneAndUpdateOperation(filter, update, options); return ExecuteWriteOperation(session, operation, cancellationToken); } - public override Task FindOneAndUpdateAsync(FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task FindOneAndUpdateAsync(FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => FindOneAndUpdateAsync(session, filter, update, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await FindOneAndUpdateAsync(session, filter, update, options, cancellationToken).ConfigureAwait(false); } - public override Task FindOneAndUpdateAsync(IClientSessionHandle session, FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override Task FindOneAndUpdateAsync(IClientSessionHandle session, FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(filter, nameof(filter)); Ensure.IsNotNull(update, nameof(update)); - options = options ?? new FindOneAndUpdateOptions(); - + options ??= new FindOneAndUpdateOptions(); if (update is PipelineUpdateDefinition && (options.ArrayFilters != null && options.ArrayFilters.Any())) { throw new NotSupportedException("An arrayfilter is not supported in the pipeline-style update."); } - var renderArgs = GetRenderArgs(); - var operation = CreateFindOneAndUpdateOperation(filter, update, options, renderArgs); + var operation = CreateFindOneAndUpdateOperation(filter, update, options); return ExecuteWriteOperationAsync(session, operation, cancellationToken); } [Obsolete("Use Aggregation pipeline instead.")] - public override IAsyncCursor MapReduce(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + public override IAsyncCursor MapReduce(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => MapReduce(session, map, reduce, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return MapReduce(session, map, reduce, options, cancellationToken: cancellationToken); } [Obsolete("Use Aggregation pipeline instead.")] - public override IAsyncCursor MapReduce(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + public override IAsyncCursor MapReduce(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(map, nameof(map)); Ensure.IsNotNull(reduce, nameof(reduce)); - options = options ?? new MapReduceOptions(); + options ??= new MapReduceOptions(); var outputOptions = options.OutputOptions ?? MapReduceOutputOptions.Inline; var resultSerializer = ResolveResultSerializer(options.ResultSerializer); @@ -615,32 +557,24 @@ public override MongoCollectionSettings Settings { var mapReduceOperation = CreateMapReduceOutputToCollectionOperation(map, reduce, options, outputOptions, renderArgs); ExecuteWriteOperation(session, mapReduceOperation, cancellationToken); - - // we want to delay execution of the find because the user may - // not want to iterate the results at all... - var findOperation = CreateMapReduceOutputToCollectionFindOperation(options, mapReduceOperation.OutputCollectionNamespace, resultSerializer); - var forkedSession = session.Fork(); - var deferredCursor = new DeferredAsyncCursor( - () => forkedSession.Dispose(), - ct => ExecuteReadOperation(forkedSession, findOperation, ReadPreference.Primary, ct), - ct => ExecuteReadOperationAsync(forkedSession, findOperation, ReadPreference.Primary, ct)); - return deferredCursor; + return CreateMapReduceOutputToCollectionResultCursor(session, options, mapReduceOperation.OutputCollectionNamespace, resultSerializer); } } [Obsolete("Use Aggregation pipeline instead.")] - public override Task> MapReduceAsync(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task> MapReduceAsync(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => MapReduceAsync(session, map, reduce, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await MapReduceAsync(session, map, reduce, options, cancellationToken).ConfigureAwait(false); } [Obsolete("Use Aggregation pipeline instead.")] - public override async Task> MapReduceAsync(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task> MapReduceAsync(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions options = null, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(map, nameof(map)); Ensure.IsNotNull(reduce, nameof(reduce)); - options = options ?? new MapReduceOptions(); + options ??= new MapReduceOptions(); var outputOptions = options.OutputOptions ?? MapReduceOutputOptions.Inline; var resultSerializer = ResolveResultSerializer(options.ResultSerializer); @@ -655,16 +589,7 @@ public override MongoCollectionSettings Settings { var mapReduceOperation = CreateMapReduceOutputToCollectionOperation(map, reduce, options, outputOptions, renderArgs); await ExecuteWriteOperationAsync(session, mapReduceOperation, cancellationToken).ConfigureAwait(false); - - // we want to delay execution of the find because the user may - // not want to iterate the results at all... - var findOperation = CreateMapReduceOutputToCollectionFindOperation(options, mapReduceOperation.OutputCollectionNamespace, resultSerializer); - var forkedSession = session.Fork(); - var deferredCursor = new DeferredAsyncCursor( - () => forkedSession.Dispose(), - ct => ExecuteReadOperation(forkedSession, findOperation, ReadPreference.Primary, ct), - ct => ExecuteReadOperationAsync(forkedSession, findOperation, ReadPreference.Primary, ct)); - return await Task.FromResult(deferredCursor).ConfigureAwait(false); + return CreateMapReduceOutputToCollectionResultCursor(session, options, mapReduceOperation.OutputCollectionNamespace, resultSerializer); } } @@ -685,42 +610,44 @@ public override IFilteredMongoCollection OfType Watch( PipelineDefinition, TResult> pipeline, ChangeStreamOptions options = null, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => Watch(session, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return Watch(session, pipeline, options, cancellationToken); } public override IChangeStreamCursor Watch( IClientSessionHandle session, PipelineDefinition, TResult> pipeline, ChangeStreamOptions options = null, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(pipeline, nameof(pipeline)); - var translationOptions = _database.Client.Settings.TranslationOptions; - var operation = CreateChangeStreamOperation(pipeline, options, translationOptions); + + var operation = CreateChangeStreamOperation(pipeline, options); return ExecuteReadOperation(session, operation, cancellationToken); } - public override Task> WatchAsync( + public override async Task> WatchAsync( PipelineDefinition, TResult> pipeline, ChangeStreamOptions options = null, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => WatchAsync(session, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await WatchAsync(session, pipeline, options, cancellationToken).ConfigureAwait(false); } public override Task> WatchAsync( IClientSessionHandle session, PipelineDefinition, TResult> pipeline, ChangeStreamOptions options = null, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(pipeline, nameof(pipeline)); - var translationOptions = _database.Client.Settings.TranslationOptions; - var operation = CreateChangeStreamOperation(pipeline, options, translationOptions); + + var operation = CreateChangeStreamOperation(pipeline, options); return ExecuteReadOperationAsync(session, operation, cancellationToken); } @@ -850,72 +777,11 @@ private AggregateOperation CreateAggregateOperation(RenderedPi }; } - private FindOperation CreateAggregateToCollectionFindOperation(BsonDocument outStage, IBsonSerializer resultSerializer, AggregateOptions options) + private IAsyncCursor CreateAggregateToCollectionResultCursor(IClientSessionHandle session, RenderedPipelineDefinition pipeline, AggregateOptions options) { - CollectionNamespace outputCollectionNamespace; - var stageName = outStage.GetElement(0).Name; - switch (stageName) - { - case "$out": - { - var outValue = outStage[0]; - DatabaseNamespace outputDatabaseNamespace; - string outputCollectionName; - if (outValue.IsString) - { - outputDatabaseNamespace = _collectionNamespace.DatabaseNamespace; - outputCollectionName = outValue.AsString; - } - else - { - outputDatabaseNamespace = new DatabaseNamespace(outValue["db"].AsString); - outputCollectionName = outValue["coll"].AsString; - } - outputCollectionNamespace = new CollectionNamespace(outputDatabaseNamespace, outputCollectionName); - } - break; - case "$merge": - { - var mergeArguments = outStage[0]; - DatabaseNamespace outputDatabaseNamespace; - string outputCollectionName; - if (mergeArguments.IsString) - { - outputDatabaseNamespace = _collectionNamespace.DatabaseNamespace; - outputCollectionName = mergeArguments.AsString; - } - else - { - var into = mergeArguments.AsBsonDocument["into"]; - if (into.IsString) - { - outputDatabaseNamespace = _collectionNamespace.DatabaseNamespace; - outputCollectionName = into.AsString; - } - else - { - if (into.AsBsonDocument.Contains("db")) - { - outputDatabaseNamespace = new DatabaseNamespace(into["db"].AsString); - } - else - { - outputDatabaseNamespace = _collectionNamespace.DatabaseNamespace; - } - outputCollectionName = into["coll"].AsString; - } - } - outputCollectionNamespace = new CollectionNamespace(outputDatabaseNamespace, outputCollectionName); - } - break; - default: - throw new ArgumentException($"Unexpected stage name: {stageName}."); - } + var outputCollectionNamespace = AggregateHelper.GetOutCollection(pipeline.Documents.Last(), _collectionNamespace.DatabaseNamespace); - return new FindOperation( - outputCollectionNamespace, - resultSerializer, - _messageEncoderSettings) + var findOperation = new FindOperation(outputCollectionNamespace, pipeline.OutputSerializer, _messageEncoderSettings) { BatchSize = options.BatchSize, Collation = options.Collation, @@ -923,6 +789,16 @@ private FindOperation CreateAggregateToCollectionFindOperation ReadConcern = _settings.ReadConcern, RetryRequested = _database.Client.Settings.RetryReads }; + + // we want to delay execution of the find because the user may + // not want to iterate the results at all... + var forkedSession = session.Fork(); + var readOperationOptions = _readOperationOptions with { ExplicitReadPreference = ReadPreference.Primary }; + var deferredCursor = new DeferredAsyncCursor( + () => forkedSession.Dispose(), + ct => ExecuteReadOperation(forkedSession, findOperation, readOperationOptions, ct), + ct => ExecuteReadOperationAsync(forkedSession, findOperation, readOperationOptions, ct)); + return deferredCursor; } private AggregateToCollectionOperation CreateAggregateToCollectionOperation(RenderedPipelineDefinition renderedPipeline, AggregateOptions options) @@ -947,15 +823,22 @@ private AggregateToCollectionOperation CreateAggregateToCollectionOperation> requests, - BulkWriteOptions options, - RenderArgs renderArgs) + IReadOnlyList> requests, + BulkWriteOptions options) { + options ??= new BulkWriteOptions(); + var renderArgs = GetRenderArgs(); var effectiveWriteConcern = session.IsInTransaction ? WriteConcern.Acknowledged : _settings.WriteConcern; + var writeModels = requests.Select((model, index) => + { + model.ThrowIfNotValid(); + return ConvertWriteModelToWriteRequest(model, index, renderArgs); + }).ToArray(); + return new BulkMixedWriteOperation( _collectionNamespace, - requests.Select((model, index) => ConvertWriteModelToWriteRequest(model, index, renderArgs)), + writeModels, _messageEncoderSettings) { BypassDocumentValidation = options.BypassDocumentValidation, @@ -969,9 +852,10 @@ private BulkMixedWriteOperation CreateBulkWriteOperation( private ChangeStreamOperation CreateChangeStreamOperation( PipelineDefinition, TResult> pipeline, - ChangeStreamOptions options, - ExpressionTranslationOptions translationOptions) + ChangeStreamOptions options) { + var translationOptions = _database.Client.Settings.TranslationOptions; + return ChangeStreamHelper.CreateChangeStreamOperation( this, pipeline, @@ -984,9 +868,11 @@ private ChangeStreamOperation CreateChangeStreamOperation( private CountDocumentsOperation CreateCountDocumentsOperation( FilterDefinition filter, - CountOptions options, - RenderArgs renderArgs) + CountOptions options) { + options ??= new CountOptions(); + var renderArgs = GetRenderArgs(); + return new CountDocumentsOperation(_collectionNamespace, _messageEncoderSettings) { Collation = options.Collation, @@ -1003,9 +889,11 @@ private CountDocumentsOperation CreateCountDocumentsOperation( private CountOperation CreateCountOperation( FilterDefinition filter, - CountOptions options, - RenderArgs renderArgs) + CountOptions options) { + options ??= new CountOptions(); + var renderArgs = GetRenderArgs(); + return new CountOperation(_collectionNamespace, _messageEncoderSettings) { Collation = options.Collation, @@ -1023,9 +911,10 @@ private CountOperation CreateCountOperation( private DistinctOperation CreateDistinctOperation( FieldDefinition field, FilterDefinition filter, - DistinctOptions options, - RenderArgs renderArgs) + DistinctOptions options) { + options ??= new DistinctOptions(); + var renderArgs = GetRenderArgs(); var renderedField = field.Render(renderArgs); var valueSerializer = GetValueSerializerForDistinct(renderedField, _settings.SerializerRegistry); @@ -1047,9 +936,10 @@ private DistinctOperation CreateDistinctOperation( private DistinctOperation CreateDistinctManyOperation( FieldDefinition> field, FilterDefinition filter, - DistinctOptions options, - RenderArgs renderArgs) + DistinctOptions options) { + options ??= new DistinctOptions(); + var renderArgs = GetRenderArgs(); var renderedField = field.Render(renderArgs); var itemSerializer = GetItemSerializerForDistinctMany(renderedField, _settings.SerializerRegistry); @@ -1080,9 +970,10 @@ private EstimatedDocumentCountOperation CreateEstimatedDocumentCountOperation(Es private FindOneAndDeleteOperation CreateFindOneAndDeleteOperation( FilterDefinition filter, - FindOneAndDeleteOptions options, - RenderArgs renderArgs) + FindOneAndDeleteOptions options) { + options ??= new FindOneAndDeleteOptions(); + var renderArgs = GetRenderArgs(); var projection = options.Projection ?? new ClientSideDeserializationProjectionDefinition(); var renderedProjection = projection.Render(renderArgs with { RenderForFind = true }); @@ -1106,17 +997,19 @@ private FindOneAndDeleteOperation CreateFindOneAndDeleteOperation CreateFindOneAndReplaceOperation( FilterDefinition filter, - object replacementObject, - FindOneAndReplaceOptions options, - RenderArgs renderArgs) + object replacement, + FindOneAndReplaceOptions options) { + options ??= new FindOneAndReplaceOptions(); + + var renderArgs = GetRenderArgs(); var projection = options.Projection ?? new ClientSideDeserializationProjectionDefinition(); var renderedProjection = projection.Render(renderArgs with { RenderForFind = true }); return new FindOneAndReplaceOperation( _collectionNamespace, filter.Render(renderArgs), - new BsonDocumentWrapper(replacementObject, _documentSerializer), + new BsonDocumentWrapper(replacement, _documentSerializer), new FindAndModifyValueDeserializer(renderedProjection.ProjectionSerializer), _messageEncoderSettings) { @@ -1138,9 +1031,9 @@ private FindOneAndReplaceOperation CreateFindOneAndReplaceOperation private FindOneAndUpdateOperation CreateFindOneAndUpdateOperation( FilterDefinition filter, UpdateDefinition update, - FindOneAndUpdateOptions options, - RenderArgs renderArgs) + FindOneAndUpdateOptions options) { + var renderArgs = GetRenderArgs(); var projection = options.Projection ?? new ClientSideDeserializationProjectionDefinition(); var renderedProjection = projection.Render(renderArgs with { RenderForFind = true }); @@ -1169,9 +1062,11 @@ private FindOneAndUpdateOperation CreateFindOneAndUpdateOperation CreateFindOperation( FilterDefinition filter, - FindOptions options, - RenderArgs renderArgs) + FindOptions options) { + options ??= new FindOptions(); + + var renderArgs = GetRenderArgs(options.TranslationOptions); var projection = options.Projection ?? new ClientSideDeserializationProjectionDefinition(); var renderedProjection = projection.Render(renderArgs with { RenderForFind = true }); @@ -1285,10 +1180,10 @@ private MapReduceOutputToCollectionOperation CreateMapReduceOutputToCollectionOp } #pragma warning disable CS0618 // Type or member is obsolete - private FindOperation CreateMapReduceOutputToCollectionFindOperation(MapReduceOptions options, CollectionNamespace outputCollectionNamespace, IBsonSerializer resultSerializer) + private IAsyncCursor CreateMapReduceOutputToCollectionResultCursor(IClientSessionHandle session, MapReduceOptions options, CollectionNamespace outputCollectionNamespace, IBsonSerializer resultSerializer) #pragma warning restore CS0618 // Type or member is obsolete { - return new FindOperation( + var findOperation = new FindOperation( outputCollectionNamespace, resultSerializer, _messageEncoderSettings) @@ -1298,22 +1193,36 @@ private FindOperation CreateMapReduceOutputToCollectionFindOperation( + () => forkedSession.Dispose(), + ct => ExecuteReadOperation(forkedSession, findOperation, readOperationOptions, ct), + ct => ExecuteReadOperationAsync(forkedSession, findOperation, readOperationOptions, ct)); + return deferredCursor; } - private IReadBindingHandle CreateReadBinding(IClientSessionHandle session, ReadPreference readPreference) - { - if (session.IsInTransaction && readPreference.ReadPreferenceMode != ReadPreferenceMode.Primary) - { - throw new InvalidOperationException("Read preference in a transaction must be primary."); - } + private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, CancellationToken cancellationToken) + => ExecuteReadOperation(session, operation, _readOperationOptions, cancellationToken); - return ChannelPinningHelper.CreateReadBinding(_cluster, session.WrappedCoreSession.Fork(), readPreference); - } + private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, ReadOperationOptions options, CancellationToken cancellationToken) + => _operationExecutor.ExecuteReadOperation(session, operation, options, true, cancellationToken); + + private Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, CancellationToken cancellationToken) + => ExecuteReadOperationAsync(session, operation, _readOperationOptions, cancellationToken); + + private Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, ReadOperationOptions options, CancellationToken cancellationToken) + => _operationExecutor.ExecuteReadOperationAsync(session, operation, options, true, cancellationToken); + + private TResult ExecuteWriteOperation(IClientSessionHandle session, IWriteOperation operation, CancellationToken cancellationToken) + => _operationExecutor.ExecuteWriteOperation(session, operation, _writeOperationOptions, true, cancellationToken); + + private Task ExecuteWriteOperationAsync(IClientSessionHandle session, IWriteOperation operation, CancellationToken cancellationToken) + => _operationExecutor.ExecuteWriteOperationAsync(session, operation, _writeOperationOptions, true, cancellationToken); - private IWriteBindingHandle CreateReadWriteBinding(IClientSessionHandle session) - { - return ChannelPinningHelper.CreateReadWriteBinding(_cluster, session.WrappedCoreSession.Fork()); - } private MessageEncoderSettings GetMessageEncoderSettings() { @@ -1389,50 +1298,6 @@ private RenderArgs GetRenderArgs(ExpressionTranslationOptions transla return new RenderArgs(_documentSerializer, _settings.SerializerRegistry, translationOptions: translationOptions); } - private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, CancellationToken cancellationToken = default(CancellationToken)) - { - var effectiveReadPreference = ReadPreferenceResolver.GetEffectiveReadPreference(session, null, _settings.ReadPreference); - return ExecuteReadOperation(session, operation, effectiveReadPreference, cancellationToken); - } - - private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, ReadPreference readPreference, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var binding = CreateReadBinding(session, readPreference)) - { - return _operationExecutor.ExecuteReadOperation(binding, operation, cancellationToken); - } - } - - private Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, CancellationToken cancellationToken = default(CancellationToken)) - { - var effectiveReadPreference = ReadPreferenceResolver.GetEffectiveReadPreference(session, null, _settings.ReadPreference); - return ExecuteReadOperationAsync(session, operation, effectiveReadPreference, cancellationToken); - } - - private async Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, ReadPreference readPreference, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var binding = CreateReadBinding(session, readPreference)) - { - return await _operationExecutor.ExecuteReadOperationAsync(binding, operation, cancellationToken).ConfigureAwait(false); - } - } - - private TResult ExecuteWriteOperation(IClientSessionHandle session, IWriteOperation operation, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var binding = CreateReadWriteBinding(session)) - { - return _operationExecutor.ExecuteWriteOperation(binding, operation, cancellationToken); - } - } - - private async Task ExecuteWriteOperationAsync(IClientSessionHandle session, IWriteOperation operation, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var binding = CreateReadWriteBinding(session)) - { - return await _operationExecutor.ExecuteWriteOperationAsync(binding, operation, cancellationToken).ConfigureAwait(false); - } - } - private IEnumerable RenderArrayFilters(IEnumerable arrayFilters) { if (arrayFilters == null) @@ -1465,38 +1330,6 @@ private IBsonSerializer ResolveResultSerializer(IBsonSerialize return _settings.SerializerRegistry.GetSerializer(); } - private void UsingImplicitSession(Action func, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var session = _operationExecutor.StartImplicitSession(cancellationToken)) - { - func(session); - } - } - - private TResult UsingImplicitSession(Func func, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var session = _operationExecutor.StartImplicitSession(cancellationToken)) - { - return func(session); - } - } - - private async Task UsingImplicitSessionAsync(Func funcAsync, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var session = await _operationExecutor.StartImplicitSessionAsync(cancellationToken).ConfigureAwait(false)) - { - await funcAsync(session).ConfigureAwait(false); - } - } - - private async Task UsingImplicitSessionAsync(Func> funcAsync, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var session = await _operationExecutor.StartImplicitSessionAsync(cancellationToken).ConfigureAwait(false)) - { - return await funcAsync(session).ConfigureAwait(false); - } - } - // nested types private class MongoIndexManager : MongoIndexManagerBase { @@ -1526,92 +1359,77 @@ public override MongoCollectionSettings Settings } // public methods - public override IEnumerable CreateMany(IEnumerable> models, CancellationToken cancellationToken = default(CancellationToken)) - { - return CreateMany(models, null, cancellationToken); - } + public override IEnumerable CreateMany(IEnumerable> models, CancellationToken cancellationToken = default) + => CreateMany(models, null, cancellationToken: cancellationToken); public override IEnumerable CreateMany( IEnumerable> models, CreateManyIndexesOptions options, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { - return _collection.UsingImplicitSession(session => CreateMany(session, models, options, cancellationToken), cancellationToken); + using var session = _collection._operationExecutor.StartImplicitSession(); + return CreateMany(session, models, options, cancellationToken); } - public override IEnumerable CreateMany(IClientSessionHandle session, IEnumerable> models, CancellationToken cancellationToken = default(CancellationToken)) - { - return CreateMany(session, models, null, cancellationToken); - } + public override IEnumerable CreateMany(IClientSessionHandle session, IEnumerable> models, CancellationToken cancellationToken = default) + => CreateMany(session, models, null, cancellationToken: cancellationToken); public override IEnumerable CreateMany( IClientSessionHandle session, IEnumerable> models, CreateManyIndexesOptions options, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(models, nameof(models)); - var renderArgs = _collection.GetRenderArgs(); - var requests = CreateCreateIndexRequests(models, renderArgs); - var operation = CreateCreateIndexesOperation(requests, options); + var operation = CreateCreateIndexesOperation(models, options); _collection.ExecuteWriteOperation(session, operation, cancellationToken); - - return requests.Select(x => x.GetIndexName()); + return operation.Requests.Select(x => x.GetIndexName()); } - public override Task> CreateManyAsync(IEnumerable> models, CancellationToken cancellationToken = default(CancellationToken)) - { - return CreateManyAsync(models, null, cancellationToken); - } + public override Task> CreateManyAsync(IEnumerable> models, CancellationToken cancellationToken = default) + => CreateManyAsync(models, null, cancellationToken: cancellationToken); - public override Task> CreateManyAsync( + public override async Task> CreateManyAsync( IEnumerable> models, CreateManyIndexesOptions options, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { - return _collection.UsingImplicitSessionAsync(session => CreateManyAsync(session, models, options, cancellationToken), cancellationToken); + using var session = _collection._operationExecutor.StartImplicitSession(); + return await CreateManyAsync(session, models, options, cancellationToken).ConfigureAwait(false); } - public override Task> CreateManyAsync(IClientSessionHandle session, IEnumerable> models, CancellationToken cancellationToken = default(CancellationToken)) - { - return CreateManyAsync(session, models, null, cancellationToken); - } + public override Task> CreateManyAsync(IClientSessionHandle session, IEnumerable> models, CancellationToken cancellationToken = default) + => CreateManyAsync(session, models, null, cancellationToken: cancellationToken); public override async Task> CreateManyAsync( IClientSessionHandle session, IEnumerable> models, CreateManyIndexesOptions options, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(models, nameof(models)); - var renderArgs = _collection.GetRenderArgs(); - var requests = CreateCreateIndexRequests(models, renderArgs); - var operation = CreateCreateIndexesOperation(requests, options); + var operation = CreateCreateIndexesOperation(models, options); await _collection.ExecuteWriteOperationAsync(session, operation, cancellationToken).ConfigureAwait(false); - - return requests.Select(x => x.GetIndexName()); + return operation.Requests.Select(x => x.GetIndexName()); } public override void DropAll(CancellationToken cancellationToken) - { - _collection.UsingImplicitSession(session => DropAll(session, cancellationToken), cancellationToken); - } + => DropAll(options: null, cancellationToken: cancellationToken); - public override void DropAll(DropIndexOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override void DropAll(DropIndexOptions options, CancellationToken cancellationToken = default) { - _collection.UsingImplicitSession(session => DropAll(session, options, cancellationToken), cancellationToken); + using var session = _collection._operationExecutor.StartImplicitSession(); + DropAll(session, options, cancellationToken); } - public override void DropAll(IClientSessionHandle session, CancellationToken cancellationToken = default(CancellationToken)) - { - DropAll(session, null, cancellationToken); - } + public override void DropAll(IClientSessionHandle session, CancellationToken cancellationToken = default) + => DropAll(session, null, cancellationToken); - public override void DropAll(IClientSessionHandle session, DropIndexOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override void DropAll(IClientSessionHandle session, DropIndexOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); var operation = CreateDropAllOperation(options); @@ -1619,138 +1437,117 @@ public override void DropAll(CancellationToken cancellationToken) } public override Task DropAllAsync(CancellationToken cancellationToken) - { - return _collection.UsingImplicitSessionAsync(session => DropAllAsync(session, cancellationToken), cancellationToken); - } + => DropAllAsync(options: null, cancellationToken); - public override Task DropAllAsync(DropIndexOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task DropAllAsync(DropIndexOptions options, CancellationToken cancellationToken = default) { - return _collection.UsingImplicitSessionAsync(session => DropAllAsync(session, options, cancellationToken), cancellationToken); + using var session = _collection._operationExecutor.StartImplicitSession(); + await DropAllAsync(session, options, cancellationToken).ConfigureAwait(false); } - public override Task DropAllAsync(IClientSessionHandle session, CancellationToken cancellationToken = default(CancellationToken)) - { - return DropAllAsync(session, null, cancellationToken); - } + public override Task DropAllAsync(IClientSessionHandle session, CancellationToken cancellationToken = default) + => DropAllAsync(session, null, cancellationToken); - public override Task DropAllAsync(IClientSessionHandle session, DropIndexOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override Task DropAllAsync(IClientSessionHandle session, DropIndexOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); var operation = CreateDropAllOperation(options); return _collection.ExecuteWriteOperationAsync(session, operation, cancellationToken); } - public override void DropOne(string name, CancellationToken cancellationToken = default(CancellationToken)) - { - _collection.UsingImplicitSession(session => DropOne(session, name, cancellationToken), cancellationToken); - } + public override void DropOne(string name, CancellationToken cancellationToken = default) + => DropOne(name, null, cancellationToken); - public override void DropOne(string name, DropIndexOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override void DropOne(string name, DropIndexOptions options, CancellationToken cancellationToken = default) { - _collection.UsingImplicitSession(session => DropOne(session, name, options, cancellationToken), cancellationToken); + using var session = _collection._operationExecutor.StartImplicitSession(); + DropOne(session, name, options, cancellationToken); } - public override void DropOne(IClientSessionHandle session, string name, CancellationToken cancellationToken = default(CancellationToken)) - { - DropOne(session, name, null, cancellationToken); - } + public override void DropOne(IClientSessionHandle session, string name, CancellationToken cancellationToken = default) + => DropOne(session, name, null, cancellationToken); - public override void DropOne( - IClientSessionHandle session, - string name, - DropIndexOptions options, - CancellationToken cancellationToken) + public override void DropOne(IClientSessionHandle session, string name, DropIndexOptions options, CancellationToken cancellationToken) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNullOrEmpty(name, nameof(name)); if (name == "*") { - throw new ArgumentException("Cannot specify '*' for the index name. Use DropAllAsync to drop all indexes.", "name"); + throw new ArgumentException($"Cannot specify '*' for the index name. Use {nameof(DropAll)} to drop all indexes.", nameof(name)); } var operation = CreateDropOneOperation(name, options); _collection.ExecuteWriteOperation(session, operation, cancellationToken); } - public override Task DropOneAsync(string name, CancellationToken cancellationToken = default(CancellationToken)) - { - return _collection.UsingImplicitSessionAsync(session => DropOneAsync(session, name, cancellationToken), cancellationToken); - } + public override Task DropOneAsync(string name, CancellationToken cancellationToken = default) + => DropOneAsync(name, null, cancellationToken); - public override Task DropOneAsync(string name, DropIndexOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task DropOneAsync(string name, DropIndexOptions options, CancellationToken cancellationToken = default) { - return _collection.UsingImplicitSessionAsync(session => DropOneAsync(session, name, options, cancellationToken), cancellationToken); + using var session = _collection._operationExecutor.StartImplicitSession(); + await DropOneAsync(session, name, options, cancellationToken).ConfigureAwait(false); } - public override Task DropOneAsync(IClientSessionHandle session, string name, CancellationToken cancellationToken = default(CancellationToken)) - { - return DropOneAsync(session, name, null, cancellationToken); - } + public override Task DropOneAsync(IClientSessionHandle session, string name, CancellationToken cancellationToken = default) + => DropOneAsync(session, name, null, cancellationToken); - public override Task DropOneAsync( - IClientSessionHandle session, - string name, - DropIndexOptions options, - CancellationToken cancellationToken) + public override Task DropOneAsync(IClientSessionHandle session, string name, DropIndexOptions options, CancellationToken cancellationToken) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNullOrEmpty(name, nameof(name)); if (name == "*") { - throw new ArgumentException("Cannot specify '*' for the index name. Use DropAllAsync to drop all indexes.", "name"); + throw new ArgumentException($"Cannot specify '*' for the index name. Use {nameof(DropAllAsync)} to drop all indexes.", nameof(name)); } var operation = CreateDropOneOperation(name, options); return _collection.ExecuteWriteOperationAsync(session, operation, cancellationToken); } - public override IAsyncCursor List(CancellationToken cancellationToken = default(CancellationToken)) - { - return List(options: null, cancellationToken); - } + public override IAsyncCursor List(CancellationToken cancellationToken = default) + => List(options: null, cancellationToken); - public override IAsyncCursor List(ListIndexesOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override IAsyncCursor List(ListIndexesOptions options, CancellationToken cancellationToken = default) { - return _collection.UsingImplicitSession(session => List(session, options, cancellationToken), cancellationToken); + using var session = _collection._operationExecutor.StartImplicitSession(); + return List(session, options, cancellationToken); } public override IAsyncCursor List(IClientSessionHandle session, CancellationToken cancellationToken = default) - { - return List(session, options: null, cancellationToken); - } + => List(session, options: null, cancellationToken); - public override IAsyncCursor List(IClientSessionHandle session, ListIndexesOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override IAsyncCursor List(IClientSessionHandle session, ListIndexesOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); var operation = CreateListIndexesOperation(options); - return _collection.ExecuteReadOperation(session, operation, ReadPreference.Primary, cancellationToken); + return _collection.ExecuteReadOperation(session, operation, cancellationToken); } - public override Task> ListAsync(CancellationToken cancellationToken = default(CancellationToken)) - { - return ListAsync(options: null, cancellationToken); - } + public override Task> ListAsync(CancellationToken cancellationToken = default) + => ListAsync(options: null, cancellationToken); - public override Task> ListAsync(ListIndexesOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task> ListAsync(ListIndexesOptions options, CancellationToken cancellationToken = default) { - return _collection.UsingImplicitSessionAsync(session => ListAsync(session, options, cancellationToken), cancellationToken); + using var session = _collection._operationExecutor.StartImplicitSession(); + return await ListAsync(session, options, cancellationToken).ConfigureAwait(false); } public override Task> ListAsync(IClientSessionHandle session, CancellationToken cancellationToken = default) - { - return ListAsync(session, options: null, cancellationToken); - } + => ListAsync(session, options: null, cancellationToken); - public override Task> ListAsync(IClientSessionHandle session, ListIndexesOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public override Task> ListAsync(IClientSessionHandle session, ListIndexesOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); var operation = CreateListIndexesOperation(options); - return _collection.ExecuteReadOperationAsync(session, operation, ReadPreference.Primary, cancellationToken); + return _collection.ExecuteReadOperationAsync(session, operation, cancellationToken); } // private methods - private CreateIndexesOperation CreateCreateIndexesOperation(IEnumerable requests, CreateManyIndexesOptions options) + private CreateIndexesOperation CreateCreateIndexesOperation(IEnumerable> models, CreateManyIndexesOptions options) { + var requests = CreateCreateIndexRequests(models); + return new CreateIndexesOperation(_collection._collectionNamespace, requests, _collection._messageEncoderSettings) { Comment = options?.Comment, @@ -1760,8 +1557,9 @@ private CreateIndexesOperation CreateCreateIndexesOperation(IEnumerable CreateCreateIndexRequests(IEnumerable> models, RenderArgs renderArgs) + private IEnumerable CreateCreateIndexRequests(IEnumerable> models) { + var renderArgs = _collection.GetRenderArgs(); return models.Select(m => { var options = m.Options ?? new CreateIndexOptions(); @@ -1841,20 +1639,18 @@ public MongoSearchIndexManager(MongoCollectionImpl collection) public IEnumerable CreateMany(IEnumerable models, CancellationToken cancellationToken = default) { + using var session = _collection._operationExecutor.StartImplicitSession(); var operation = CreateCreateIndexesOperation(models); - var result = _collection.UsingImplicitSession(session => _collection.ExecuteWriteOperation(session, operation, cancellationToken), cancellationToken); - var indexNames = GetIndexNames(result); - - return indexNames; + var result = _collection.ExecuteWriteOperation(session, operation, cancellationToken); + return GetIndexNames(result); } public async Task> CreateManyAsync(IEnumerable models, CancellationToken cancellationToken = default) { + using var session = _collection._operationExecutor.StartImplicitSession(); var operation = CreateCreateIndexesOperation(models); - var result = await _collection.UsingImplicitSessionAsync(session => _collection.ExecuteWriteOperationAsync(session, operation, cancellationToken), cancellationToken).ConfigureAwait(false); - var indexNames = GetIndexNames(result); - - return indexNames; + var result = await _collection.ExecuteWriteOperationAsync(session, operation, cancellationToken).ConfigureAwait(false); + return GetIndexNames(result); } public string CreateOne(BsonDocument definition, string name = null, CancellationToken cancellationToken = default) => @@ -1877,14 +1673,16 @@ public async Task CreateOneAsync(CreateSearchIndexModel model, Cancellat public void DropOne(string indexName, CancellationToken cancellationToken = default) { + using var session = _collection._operationExecutor.StartImplicitSession(); var operation = new DropSearchIndexOperation(_collection.CollectionNamespace, indexName, _collection._messageEncoderSettings); - _collection.UsingImplicitSession(session => _collection.ExecuteWriteOperation(session, operation, cancellationToken), cancellationToken); + _collection.ExecuteWriteOperation(session, operation, cancellationToken); } - public Task DropOneAsync(string indexName, CancellationToken cancellationToken = default) + public async Task DropOneAsync(string indexName, CancellationToken cancellationToken = default) { + using var session = _collection._operationExecutor.StartImplicitSession(); var operation = new DropSearchIndexOperation(_collection.CollectionNamespace, indexName, _collection._messageEncoderSettings); - return _collection.UsingImplicitSessionAsync(session => _collection.ExecuteWriteOperationAsync(session, operation, cancellationToken), cancellationToken); + await _collection.ExecuteWriteOperationAsync(session, operation, cancellationToken).ConfigureAwait(false); } public IAsyncCursor List(string indexName, AggregateOptions aggregateOptions = null, CancellationToken cancellationToken = default) @@ -1899,16 +1697,16 @@ public Task> ListAsync(string indexName, AggregateOpt public void Update(string indexName, BsonDocument definition, CancellationToken cancellationToken = default) { + using var session = _collection._operationExecutor.StartImplicitSession(); var operation = new UpdateSearchIndexOperation(_collection.CollectionNamespace, indexName, definition, _collection._messageEncoderSettings); - - _collection.UsingImplicitSession(session => _collection.ExecuteWriteOperation(session, operation, cancellationToken), cancellationToken); + _collection.ExecuteWriteOperation(session, operation, cancellationToken); } public async Task UpdateAsync(string indexName, BsonDocument definition, CancellationToken cancellationToken = default) { + using var session = _collection._operationExecutor.StartImplicitSession(); var operation = new UpdateSearchIndexOperation(_collection.CollectionNamespace, indexName, definition, _collection._messageEncoderSettings); - - await _collection.UsingImplicitSessionAsync(session => _collection.ExecuteWriteOperationAsync(session, operation, cancellationToken), cancellationToken).ConfigureAwait(false); + await _collection.ExecuteWriteOperationAsync(session, operation, cancellationToken).ConfigureAwait(false); } // private methods diff --git a/src/MongoDB.Driver/MongoDatabase.cs b/src/MongoDB.Driver/MongoDatabase.cs index a57eec55d94..cb1e0820dff 100644 --- a/src/MongoDB.Driver/MongoDatabase.cs +++ b/src/MongoDB.Driver/MongoDatabase.cs @@ -23,8 +23,6 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Driver.Core; -using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Operations; @@ -41,6 +39,8 @@ internal sealed class MongoDatabase : IMongoDatabase private readonly DatabaseNamespace _databaseNamespace; private readonly IOperationExecutor _operationExecutor; private readonly MongoDatabaseSettings _settings; + private readonly ReadOperationOptions _readOperationOptions; + private readonly WriteOperationOptions _writeOperationOptions; // constructors public MongoDatabase(IMongoClient client, DatabaseNamespace databaseNamespace, MongoDatabaseSettings settings, IClusterInternal cluster, IOperationExecutor operationExecutor) @@ -50,6 +50,9 @@ public MongoDatabase(IMongoClient client, DatabaseNamespace databaseNamespace, M _settings = Ensure.IsNotNull(settings, nameof(settings)).Freeze(); _cluster = Ensure.IsNotNull(cluster, nameof(cluster)); _operationExecutor = Ensure.IsNotNull(operationExecutor, nameof(operationExecutor)); + + _readOperationOptions = new(DefaultReadPreference: _settings.ReadPreference); + _writeOperationOptions = new(); } // public properties @@ -58,34 +61,25 @@ public MongoDatabase(IMongoClient client, DatabaseNamespace databaseNamespace, M public MongoDatabaseSettings Settings => _settings; // public methods - public IAsyncCursor Aggregate(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public IAsyncCursor Aggregate(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => Aggregate(session, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return Aggregate(session, pipeline, options, cancellationToken: cancellationToken); } - public IAsyncCursor Aggregate(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public IAsyncCursor Aggregate(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); - var renderArgs = GetRenderArgs(NoPipelineInputSerializer.Instance, options?.TranslationOptions); - var renderedPipeline = Ensure.IsNotNull(pipeline, nameof(pipeline)).Render(renderArgs); - options = options ?? new AggregateOptions(); + Ensure.IsNotNull(pipeline, nameof(pipeline)); + options ??= new AggregateOptions(); - var lastStage = renderedPipeline.Documents.LastOrDefault(); - var lastStageName = lastStage?.GetElement(0).Name; - if (lastStage != null && (lastStageName == "$out" || lastStageName == "$merge")) + var renderArgs = GetRenderArgs(NoPipelineInputSerializer.Instance, options.TranslationOptions); + var renderedPipeline = AggregateHelper.RenderAggregatePipeline(pipeline, renderArgs, out var isAggregateToCollection); + if (isAggregateToCollection) { var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); ExecuteWriteOperation(session, aggregateOperation, cancellationToken); - - // we want to delay execution of the find because the user may - // not want to iterate the results at all... - var findOperation = CreateAggregateToCollectionFindOperation(lastStage, renderedPipeline.OutputSerializer, options); - var forkedSession = session.Fork(); - var deferredCursor = new DeferredAsyncCursor( - () => forkedSession.Dispose(), - ct => ExecuteReadOperation(forkedSession, findOperation, ReadPreference.Primary, ct), - ct => ExecuteReadOperationAsync(forkedSession, findOperation, ReadPreference.Primary, ct)); - return deferredCursor; + return CreateAggregateToCollectionResultCursor(session, renderedPipeline, options); } else { @@ -94,34 +88,25 @@ public MongoDatabase(IMongoClient client, DatabaseNamespace databaseNamespace, M } } - public Task> AggregateAsync(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public async Task> AggregateAsync(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => AggregateAsync(session, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await AggregateAsync(session, pipeline, options, cancellationToken).ConfigureAwait(false); } - public async Task> AggregateAsync(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public async Task> AggregateAsync(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); - var renderArgs = GetRenderArgs(NoPipelineInputSerializer.Instance, options?.TranslationOptions); - var renderedPipeline = Ensure.IsNotNull(pipeline, nameof(pipeline)).Render(renderArgs); - options = options ?? new AggregateOptions(); + Ensure.IsNotNull(pipeline, nameof(pipeline)); + options ??= new AggregateOptions(); - var lastStage = renderedPipeline.Documents.LastOrDefault(); - var lastStageName = lastStage?.GetElement(0).Name; - if (lastStage != null && (lastStageName == "$out" || lastStageName == "$merge")) + var renderArgs = GetRenderArgs(NoPipelineInputSerializer.Instance, options.TranslationOptions); + var renderedPipeline = AggregateHelper.RenderAggregatePipeline(pipeline, renderArgs, out var isAggregateToCollection); + if (isAggregateToCollection) { var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); await ExecuteWriteOperationAsync(session, aggregateOperation, cancellationToken).ConfigureAwait(false); - - // we want to delay execution of the find because the user may - // not want to iterate the results at all... - var findOperation = CreateAggregateToCollectionFindOperation(lastStage, renderedPipeline.OutputSerializer, options); - var forkedSession = session.Fork(); - var deferredCursor = new DeferredAsyncCursor( - () => forkedSession.Dispose(), - ct => ExecuteReadOperation(forkedSession, findOperation, ReadPreference.Primary, ct), - ct => ExecuteReadOperationAsync(forkedSession, findOperation, ReadPreference.Primary, ct)); - return await Task.FromResult>(deferredCursor).ConfigureAwait(false); + return CreateAggregateToCollectionResultCursor(session, renderedPipeline, options); } else { @@ -130,59 +115,56 @@ public MongoDatabase(IMongoClient client, DatabaseNamespace databaseNamespace, M } } - public void AggregateToCollection(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public void AggregateToCollection(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { - UsingImplicitSession(session => AggregateToCollection(session, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + AggregateToCollection(session, pipeline, options, cancellationToken); } - public void AggregateToCollection(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public void AggregateToCollection(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); - var renderArgs = GetRenderArgs(NoPipelineInputSerializer.Instance, options?.TranslationOptions); - var renderedPipeline = Ensure.IsNotNull(pipeline, nameof(pipeline)).Render(renderArgs); - options = options ?? new AggregateOptions(); + Ensure.IsNotNull(pipeline, nameof(pipeline)); + options ??= new AggregateOptions(); - var lastStage = renderedPipeline.Documents.LastOrDefault(); - var lastStageName = lastStage?.GetElement(0).Name; - if (lastStage == null || (lastStageName != "$out" && lastStageName != "$merge")) + var renderArgs = GetRenderArgs(NoPipelineInputSerializer.Instance, options.TranslationOptions); + var renderedPipeline = AggregateHelper.RenderAggregatePipeline(pipeline, renderArgs, out var isAggregateToCollection); + if (!isAggregateToCollection) { throw new InvalidOperationException("AggregateToCollection requires that the last stage be $out or $merge."); } - else - { - var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); - ExecuteWriteOperation(session, aggregateOperation, cancellationToken); - } + + var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); + ExecuteWriteOperation(session, aggregateOperation, cancellationToken); } - public Task AggregateToCollectionAsync(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public async Task AggregateToCollectionAsync(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => AggregateToCollectionAsync(session, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + await AggregateToCollectionAsync(session, pipeline, options, cancellationToken).ConfigureAwait(false); } - public async Task AggregateToCollectionAsync(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken)) + public Task AggregateToCollectionAsync(IClientSessionHandle session, PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); - var renderArgs = GetRenderArgs(NoPipelineInputSerializer.Instance, options?.TranslationOptions); - var renderedPipeline = Ensure.IsNotNull(pipeline, nameof(pipeline)).Render(renderArgs); - options = options ?? new AggregateOptions(); + Ensure.IsNotNull(pipeline, nameof(pipeline)); + options ??= new AggregateOptions(); - var lastStage = renderedPipeline.Documents.LastOrDefault(); - var lastStageName = lastStage?.GetElement(0).Name; - if (lastStage == null || (lastStageName != "$out" && lastStageName != "$merge")) + var renderArgs = GetRenderArgs(NoPipelineInputSerializer.Instance, options.TranslationOptions); + var renderedPipeline = AggregateHelper.RenderAggregatePipeline(pipeline, renderArgs, out var isAggregateToCollection); + if (!isAggregateToCollection) { throw new InvalidOperationException("AggregateToCollectionAsync requires that the last stage be $out or $merge."); } - else - { - var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); - await ExecuteWriteOperationAsync(session, aggregateOperation, cancellationToken).ConfigureAwait(false); - } + + var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); + return ExecuteWriteOperationAsync(session, aggregateOperation, cancellationToken); } public void CreateCollection(string name, CreateCollectionOptions options, CancellationToken cancellationToken) { - UsingImplicitSession(session => CreateCollection(session, name, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + CreateCollection(session, name, options, cancellationToken); } public void CreateCollection(IClientSessionHandle session, string name, CreateCollectionOptions options, CancellationToken cancellationToken) @@ -203,7 +185,7 @@ public void CreateCollection(IClientSessionHandle session, string name, CreateCo return; } - var genericMethodDefinition = typeof(MongoDatabase).GetTypeInfo().GetMethod("CreateCollectionHelper", BindingFlags.NonPublic | BindingFlags.Instance); + var genericMethodDefinition = typeof(MongoDatabase).GetTypeInfo().GetMethod(nameof(CreateCollectionHelper), BindingFlags.NonPublic | BindingFlags.Instance); var documentType = options.GetType().GetTypeInfo().GetGenericArguments()[0]; var methodInfo = genericMethodDefinition.MakeGenericMethod(documentType); try @@ -216,9 +198,10 @@ public void CreateCollection(IClientSessionHandle session, string name, CreateCo } } - public Task CreateCollectionAsync(string name, CreateCollectionOptions options, CancellationToken cancellationToken) + public async Task CreateCollectionAsync(string name, CreateCollectionOptions options, CancellationToken cancellationToken) { - return UsingImplicitSessionAsync(session => CreateCollectionAsync(session, name, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + await CreateCollectionAsync(session, name, options, cancellationToken).ConfigureAwait(false); } public async Task CreateCollectionAsync(IClientSessionHandle session, string name, CreateCollectionOptions options, CancellationToken cancellationToken) @@ -239,7 +222,7 @@ public async Task CreateCollectionAsync(IClientSessionHandle session, string nam return; } - var genericMethodDefinition = typeof(MongoDatabase).GetTypeInfo().GetMethod("CreateCollectionHelperAsync", BindingFlags.NonPublic | BindingFlags.Instance); + var genericMethodDefinition = typeof(MongoDatabase).GetTypeInfo().GetMethod(nameof(CreateCollectionHelperAsync), BindingFlags.NonPublic | BindingFlags.Instance); var documentType = options.GetType().GetTypeInfo().GetGenericArguments()[0]; var methodInfo = genericMethodDefinition.MakeGenericMethod(documentType); try @@ -252,37 +235,37 @@ public async Task CreateCollectionAsync(IClientSessionHandle session, string nam } } - public void CreateView(string viewName, string viewOn, PipelineDefinition pipeline, CreateViewOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + public void CreateView(string viewName, string viewOn, PipelineDefinition pipeline, CreateViewOptions options = null, CancellationToken cancellationToken = default) { - UsingImplicitSession(session => CreateView(session, viewName, viewOn, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + CreateView(session, viewName, viewOn, pipeline, options, cancellationToken); } - public void CreateView(IClientSessionHandle session, string viewName, string viewOn, PipelineDefinition pipeline, CreateViewOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + public void CreateView(IClientSessionHandle session, string viewName, string viewOn, PipelineDefinition pipeline, CreateViewOptions options = null, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(viewName, nameof(viewName)); Ensure.IsNotNull(viewOn, nameof(viewOn)); Ensure.IsNotNull(pipeline, nameof(pipeline)); - options = options ?? new CreateViewOptions(); - var translationOptions = _client.Settings.TranslationOptions; - var operation = CreateCreateViewOperation(viewName, viewOn, pipeline, options, translationOptions); + + var operation = CreateCreateViewOperation(viewName, viewOn, pipeline, options); ExecuteWriteOperation(session, operation, cancellationToken); } - public Task CreateViewAsync(string viewName, string viewOn, PipelineDefinition pipeline, CreateViewOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + public async Task CreateViewAsync(string viewName, string viewOn, PipelineDefinition pipeline, CreateViewOptions options = null, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => CreateViewAsync(session, viewName, viewOn, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + await CreateViewAsync(session, viewName, viewOn, pipeline, options, cancellationToken).ConfigureAwait(false); } - public Task CreateViewAsync(IClientSessionHandle session, string viewName, string viewOn, PipelineDefinition pipeline, CreateViewOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + public Task CreateViewAsync(IClientSessionHandle session, string viewName, string viewOn, PipelineDefinition pipeline, CreateViewOptions options = null, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(viewName, nameof(viewName)); Ensure.IsNotNull(viewOn, nameof(viewOn)); Ensure.IsNotNull(pipeline, nameof(pipeline)); - options = options ?? new CreateViewOptions(); - var translationOptions = _client.Settings.TranslationOptions; - var operation = CreateCreateViewOperation(viewName, viewOn, pipeline, options, translationOptions); + + var operation = CreateCreateViewOperation(viewName, viewOn, pipeline, options); return ExecuteWriteOperationAsync(session, operation, cancellationToken); } @@ -293,7 +276,8 @@ public void DropCollection(string name, CancellationToken cancellationToken) public void DropCollection(string name, DropCollectionOptions options, CancellationToken cancellationToken = default) { - UsingImplicitSession(session => DropCollection(session, name, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + DropCollection(session, name, options, cancellationToken); } public void DropCollection(IClientSessionHandle session, string name, CancellationToken cancellationToken) @@ -305,30 +289,33 @@ public void DropCollection(IClientSessionHandle session, string name, DropCollec { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNullOrEmpty(name, nameof(name)); - var operation = CreateDropCollectionOperation(name, options, session, cancellationToken); + + var collectionNamespace = new CollectionNamespace(_databaseNamespace, name); + var encryptedFields = GetEffectiveEncryptedFields(session, collectionNamespace, options, cancellationToken); + var operation = CreateDropCollectionOperation(collectionNamespace, encryptedFields); ExecuteWriteOperation(session, operation, cancellationToken); } public Task DropCollectionAsync(string name, CancellationToken cancellationToken) - { - return DropCollectionAsync(name, options: null, cancellationToken); - } + => DropCollectionAsync(name, options: null, cancellationToken); - public Task DropCollectionAsync(string name, DropCollectionOptions options, CancellationToken cancellationToken) + public async Task DropCollectionAsync(string name, DropCollectionOptions options, CancellationToken cancellationToken) { - return UsingImplicitSessionAsync(session => DropCollectionAsync(session, name, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + await DropCollectionAsync(session, name, options, cancellationToken).ConfigureAwait(false); } public Task DropCollectionAsync(IClientSessionHandle session, string name, CancellationToken cancellationToken) - { - return DropCollectionAsync(session, name, options: null, cancellationToken); - } + => DropCollectionAsync(session, name, options: null, cancellationToken); public async Task DropCollectionAsync(IClientSessionHandle session, string name, DropCollectionOptions options, CancellationToken cancellationToken) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNullOrEmpty(name, nameof(name)); - var operation = await CreateDropCollectionOperationAsync(name, options, session, cancellationToken).ConfigureAwait(false); + + var collectionNamespace = new CollectionNamespace(_databaseNamespace, name); + var encryptedFields = await GetEffectiveEncryptedFieldsAsync(session, collectionNamespace, options, cancellationToken).ConfigureAwait(false); + var operation = CreateDropCollectionOperation(collectionNamespace, encryptedFields); await ExecuteWriteOperationAsync(session, operation, cancellationToken).ConfigureAwait(false); } @@ -345,67 +332,64 @@ public IMongoCollection GetCollection(string name, MongoCo return new MongoCollectionImpl(this, new CollectionNamespace(_databaseNamespace, name), settings, _cluster, _operationExecutor); } - public IAsyncCursor ListCollectionNames(ListCollectionNamesOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + public IAsyncCursor ListCollectionNames(ListCollectionNamesOptions options = null, CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => ListCollectionNames(session, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return ListCollectionNames(session, options, cancellationToken); } - public IAsyncCursor ListCollectionNames(IClientSessionHandle session, ListCollectionNamesOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + public IAsyncCursor ListCollectionNames(IClientSessionHandle session, ListCollectionNamesOptions options = null, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); - var renderArgs = GetRenderArgs(BsonDocumentSerializer.Instance); - var operation = CreateListCollectionNamesOperation(options, renderArgs); - var effectiveReadPreference = ReadPreferenceResolver.GetEffectiveReadPreference(session, null, ReadPreference.Primary); - var cursor = ExecuteReadOperation(session, operation, effectiveReadPreference, cancellationToken); + var operation = CreateListCollectionNamesOperation(options); + var cursor = ExecuteReadOperation(session, operation, _readOperationOptions with { DefaultReadPreference = ReadPreference.Primary }, cancellationToken); return new BatchTransformingAsyncCursor(cursor, ExtractCollectionNames); } - public Task> ListCollectionNamesAsync(ListCollectionNamesOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + public async Task> ListCollectionNamesAsync(ListCollectionNamesOptions options = null, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => ListCollectionNamesAsync(session, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await ListCollectionNamesAsync(session, options, cancellationToken).ConfigureAwait(false); } - public async Task> ListCollectionNamesAsync(IClientSessionHandle session, ListCollectionNamesOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + public async Task> ListCollectionNamesAsync(IClientSessionHandle session, ListCollectionNamesOptions options = null, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); - var renderArgs = GetRenderArgs(BsonDocumentSerializer.Instance); - var operation = CreateListCollectionNamesOperation(options, renderArgs); - var effectiveReadPreference = ReadPreferenceResolver.GetEffectiveReadPreference(session, null, ReadPreference.Primary); - var cursor = await ExecuteReadOperationAsync(session, operation, effectiveReadPreference, cancellationToken).ConfigureAwait(false); + var operation = CreateListCollectionNamesOperation(options); + var cursor = await ExecuteReadOperationAsync(session, operation, _readOperationOptions with { DefaultReadPreference = ReadPreference.Primary }, cancellationToken).ConfigureAwait(false); return new BatchTransformingAsyncCursor(cursor, ExtractCollectionNames); } public IAsyncCursor ListCollections(ListCollectionsOptions options, CancellationToken cancellationToken) { - return UsingImplicitSession(session => ListCollections(session, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return ListCollections(session, options, cancellationToken); } public IAsyncCursor ListCollections(IClientSessionHandle session, ListCollectionsOptions options, CancellationToken cancellationToken) { Ensure.IsNotNull(session, nameof(session)); - var renderArgs = GetRenderArgs(BsonDocumentSerializer.Instance); - var operation = CreateListCollectionsOperation(options, renderArgs); - var effectiveReadPreference = ReadPreferenceResolver.GetEffectiveReadPreference(session, null, ReadPreference.Primary); - return ExecuteReadOperation(session, operation, effectiveReadPreference, cancellationToken); + var operation = CreateListCollectionsOperation(options); + return ExecuteReadOperation(session, operation, _readOperationOptions with { DefaultReadPreference = ReadPreference.Primary }, cancellationToken); } - public Task> ListCollectionsAsync(ListCollectionsOptions options, CancellationToken cancellationToken) + public async Task> ListCollectionsAsync(ListCollectionsOptions options, CancellationToken cancellationToken) { - return UsingImplicitSessionAsync(session => ListCollectionsAsync(session, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await ListCollectionsAsync(session, options, cancellationToken).ConfigureAwait(false); } public Task> ListCollectionsAsync(IClientSessionHandle session, ListCollectionsOptions options, CancellationToken cancellationToken) { Ensure.IsNotNull(session, nameof(session)); - var renderArgs = GetRenderArgs(BsonDocumentSerializer.Instance); - var operation = CreateListCollectionsOperation(options, renderArgs); - var effectiveReadPreference = ReadPreferenceResolver.GetEffectiveReadPreference(session, null, ReadPreference.Primary); - return ExecuteReadOperationAsync(session, operation, effectiveReadPreference, cancellationToken); + var operation = CreateListCollectionsOperation(options); + return ExecuteReadOperationAsync(session, operation, _readOperationOptions with { DefaultReadPreference = ReadPreference.Primary }, cancellationToken); } public void RenameCollection(string oldName, string newName, RenameCollectionOptions options, CancellationToken cancellationToken) { - UsingImplicitSession(session => RenameCollection(session, oldName, newName, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + RenameCollection(session, oldName, newName, options, cancellationToken); } public void RenameCollection(IClientSessionHandle session, string oldName, string newName, RenameCollectionOptions options, CancellationToken cancellationToken) @@ -413,15 +397,15 @@ public void RenameCollection(IClientSessionHandle session, string oldName, strin Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNullOrEmpty(oldName, nameof(oldName)); Ensure.IsNotNullOrEmpty(newName, nameof(newName)); - options = options ?? new RenameCollectionOptions(); var operation = CreateRenameCollectionOperation(oldName, newName, options); ExecuteWriteOperation(session, operation, cancellationToken); } - public Task RenameCollectionAsync(string oldName, string newName, RenameCollectionOptions options, CancellationToken cancellationToken) + public async Task RenameCollectionAsync(string oldName, string newName, RenameCollectionOptions options, CancellationToken cancellationToken) { - return UsingImplicitSessionAsync(session => RenameCollectionAsync(session, oldName, newName, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + await RenameCollectionAsync(session, oldName, newName, options, cancellationToken).ConfigureAwait(false); } public Task RenameCollectionAsync(IClientSessionHandle session, string oldName, string newName, RenameCollectionOptions options, CancellationToken cancellationToken) @@ -429,81 +413,82 @@ public Task RenameCollectionAsync(IClientSessionHandle session, string oldName, Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNullOrEmpty(oldName, nameof(oldName)); Ensure.IsNotNullOrEmpty(newName, nameof(newName)); - options = options ?? new RenameCollectionOptions(); var operation = CreateRenameCollectionOperation(oldName, newName, options); return ExecuteWriteOperationAsync(session, operation, cancellationToken); } - public TResult RunCommand(Command command, ReadPreference readPreference = null, CancellationToken cancellationToken = default(CancellationToken)) + public TResult RunCommand(Command command, ReadPreference readPreference = null, CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => RunCommand(session, command, readPreference, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return RunCommand(session, command, readPreference, cancellationToken); } - public TResult RunCommand(IClientSessionHandle session, Command command, ReadPreference readPreference = null, CancellationToken cancellationToken = default(CancellationToken)) + public TResult RunCommand(IClientSessionHandle session, Command command, ReadPreference readPreference = null, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(command, nameof(command)); var operation = CreateRunCommandOperation(command); - var effectiveReadPreference = ReadPreferenceResolver.GetEffectiveReadPreference(session, readPreference, ReadPreference.Primary); - return ExecuteReadOperation(session, operation, effectiveReadPreference, cancellationToken); + return ExecuteReadOperation(session, operation, _readOperationOptions with { ExplicitReadPreference = readPreference, DefaultReadPreference = ReadPreference.Primary }, cancellationToken); } - public Task RunCommandAsync(Command command, ReadPreference readPreference = null, CancellationToken cancellationToken = default(CancellationToken)) + public async Task RunCommandAsync(Command command, ReadPreference readPreference = null, CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => RunCommandAsync(session, command, readPreference, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await RunCommandAsync(session, command, readPreference, cancellationToken).ConfigureAwait(false); } - public Task RunCommandAsync(IClientSessionHandle session, Command command, ReadPreference readPreference = null, CancellationToken cancellationToken = default(CancellationToken)) + public Task RunCommandAsync(IClientSessionHandle session, Command command, ReadPreference readPreference = null, CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(command, nameof(command)); var operation = CreateRunCommandOperation(command); - var effectiveReadPreference = ReadPreferenceResolver.GetEffectiveReadPreference(session, readPreference, ReadPreference.Primary); - return ExecuteReadOperationAsync(session, operation, effectiveReadPreference, cancellationToken); + return ExecuteReadOperationAsync(session, operation, _readOperationOptions with { ExplicitReadPreference = readPreference, DefaultReadPreference = ReadPreference.Primary }, cancellationToken); } public IChangeStreamCursor Watch( PipelineDefinition, TResult> pipeline, ChangeStreamOptions options = null, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { - return UsingImplicitSession(session => Watch(session, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return Watch(session, pipeline, options, cancellationToken); } public IChangeStreamCursor Watch( IClientSessionHandle session, PipelineDefinition, TResult> pipeline, ChangeStreamOptions options = null, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(pipeline, nameof(pipeline)); - var translationOptions = _client.Settings.TranslationOptions; - var operation = CreateChangeStreamOperation(pipeline, options, translationOptions); + + var operation = CreateChangeStreamOperation(pipeline, options); return ExecuteReadOperation(session, operation, cancellationToken); } - public Task> WatchAsync( + public async Task> WatchAsync( PipelineDefinition, TResult> pipeline, ChangeStreamOptions options = null, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { - return UsingImplicitSessionAsync(session => WatchAsync(session, pipeline, options, cancellationToken), cancellationToken); + using var session = _operationExecutor.StartImplicitSession(); + return await WatchAsync(session, pipeline, options, cancellationToken).ConfigureAwait(false); } public Task> WatchAsync( IClientSessionHandle session, PipelineDefinition, TResult> pipeline, ChangeStreamOptions options = null, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { Ensure.IsNotNull(session, nameof(session)); Ensure.IsNotNull(pipeline, nameof(pipeline)); - var translationOptions = _client.Settings.TranslationOptions; - var operation = CreateChangeStreamOperation(pipeline, options, translationOptions); + + var operation = CreateChangeStreamOperation(pipeline, options); return ExecuteReadOperationAsync(session, operation, cancellationToken); } @@ -532,6 +517,7 @@ public IMongoDatabase WithWriteConcern(WriteConcern writeConcern) } // private methods + private AggregateOperation CreateAggregateOperation(RenderedPipelineDefinition renderedPipeline, AggregateOptions options) { var messageEncoderSettings = GetMessageEncoderSettings(); @@ -557,59 +543,16 @@ private AggregateOperation CreateAggregateOperation(RenderedPi }; } - private FindOperation CreateAggregateToCollectionFindOperation(BsonDocument outStage, IBsonSerializer resultSerializer, AggregateOptions options) + private IAsyncCursor CreateAggregateToCollectionResultCursor(IClientSessionHandle session, RenderedPipelineDefinition pipeline, AggregateOptions options) { - CollectionNamespace outputCollectionNamespace; - var stageName = outStage.GetElement(0).Name; - switch (stageName) - { - case "$out": - { - var outValue = outStage[0]; - DatabaseNamespace outputDatabaseNamespace; - string outputCollectionName; - if (outValue.IsString) - { - outputDatabaseNamespace = _databaseNamespace; - outputCollectionName = outValue.AsString; - } - else - { - outputDatabaseNamespace = new DatabaseNamespace(outValue["db"].AsString); - outputCollectionName = outValue["coll"].AsString; - } - outputCollectionNamespace = new CollectionNamespace(outputDatabaseNamespace, outputCollectionName); - } - break; - case "$merge": - { - var mergeArguments = outStage[0].AsBsonDocument; - DatabaseNamespace outputDatabaseNamespace; - string outputCollectionName; - var into = mergeArguments["into"]; - if (into.IsString) - { - outputDatabaseNamespace = _databaseNamespace; - outputCollectionName = into.AsString; - } - else - { - outputDatabaseNamespace = new DatabaseNamespace(into["db"].AsString); - outputCollectionName = into["coll"].AsString; - } - outputCollectionNamespace = new CollectionNamespace(outputDatabaseNamespace, outputCollectionName); - } - break; - default: - throw new ArgumentException($"Unexpected stage name: {stageName}."); - } + var outputCollectionNamespace = AggregateHelper.GetOutCollection(pipeline.Documents.Last(), _databaseNamespace); // because auto encryption is not supported for non-collection commands. // So, an error will be thrown in the previous CreateAggregateToCollectionOperation step. // However, since we've added encryption configuration for CreateAggregateToCollectionOperation operation, // it's not superfluous to also add it here var messageEncoderSettings = GetMessageEncoderSettings(); - return new FindOperation(outputCollectionNamespace, resultSerializer, messageEncoderSettings) + var findOperation = new FindOperation(outputCollectionNamespace, pipeline.OutputSerializer, messageEncoderSettings) { BatchSize = options.BatchSize, Collation = options.Collation, @@ -617,6 +560,16 @@ private FindOperation CreateAggregateToCollectionFindOperation ReadConcern = _settings.ReadConcern, RetryRequested = _client.Settings.RetryReads }; + + // we want to delay execution of the find because the user may + // not want to iterate the results at all... + var forkedSession = session.Fork(); + var readOperationOptions = _readOperationOptions with { ExplicitReadPreference = ReadPreference.Primary }; + var deferredCursor = new DeferredAsyncCursor( + () => forkedSession.Dispose(), + ct => ExecuteReadOperation(forkedSession, findOperation, readOperationOptions, ct), + ct => ExecuteReadOperationAsync(forkedSession, findOperation, readOperationOptions, ct)); + return deferredCursor; } private AggregateToCollectionOperation CreateAggregateToCollectionOperation(RenderedPipelineDefinition renderedPipeline, AggregateOptions options) @@ -642,24 +595,20 @@ private AggregateToCollectionOperation CreateAggregateToCollectionOperation(IClientSessionHandle session, string name, CreateCollectionOptions options, CancellationToken cancellationToken) { - options = options ?? new CreateCollectionOptions(); - - var translationOptions = _client.Settings.TranslationOptions; - var operation = CreateCreateCollectionOperation(name, options, translationOptions); + var operation = CreateCreateCollectionOperation(name, options); ExecuteWriteOperation(session, operation, cancellationToken); } private Task CreateCollectionHelperAsync(IClientSessionHandle session, string name, CreateCollectionOptions options, CancellationToken cancellationToken) { - options = options ?? new CreateCollectionOptions(); - - var translationOptions = _client.Settings.TranslationOptions; - var operation = CreateCreateCollectionOperation(name, options, translationOptions); + var operation = CreateCreateCollectionOperation(name, options); return ExecuteWriteOperationAsync(session, operation, cancellationToken); } - private IWriteOperation CreateCreateCollectionOperation(string name, CreateCollectionOptions options, ExpressionTranslationOptions translationOptions) + private IWriteOperation CreateCreateCollectionOperation(string name, CreateCollectionOptions options) { + options ??= new CreateCollectionOptions(); + var translationOptions = _client.Settings.TranslationOptions; var serializerRegistry = options.SerializerRegistry ?? BsonSerializer.SerializerRegistry; var documentSerializer = options.DocumentSerializer ?? serializerRegistry.GetSerializer(); @@ -700,9 +649,11 @@ private CreateViewOperation CreateCreateViewOperation( string viewName, string viewOn, PipelineDefinition pipeline, - CreateViewOptions options, - ExpressionTranslationOptions translationOptions) + CreateViewOptions options) { + options ??= new CreateViewOptions(); + + var translationOptions = _client.Settings.TranslationOptions; var serializerRegistry = options.SerializerRegistry ?? BsonSerializer.SerializerRegistry; var documentSerializer = options.DocumentSerializer ?? serializerRegistry.GetSerializer(); var pipelineDocuments = pipeline.Render(new (documentSerializer, serializerRegistry, translationOptions: translationOptions)).Documents; @@ -713,60 +664,8 @@ private CreateViewOperation CreateCreateViewOperation( }; } - private IWriteOperation CreateDropCollectionOperation(string name, DropCollectionOptions options, IClientSessionHandle session, CancellationToken cancellationToken) - { - var collectionNamespace = new CollectionNamespace(_databaseNamespace, name); - - options = options ?? new DropCollectionOptions(); - - var encryptedFieldsMap = _client.Settings?.AutoEncryptionOptions?.EncryptedFieldsMap; - if (!EncryptedCollectionHelper.TryGetEffectiveEncryptedFields(collectionNamespace, options.EncryptedFields, encryptedFieldsMap, out var effectiveEncryptedFields)) - { - if (encryptedFieldsMap != null) - { - var listCollectionOptions = new ListCollectionsOptions() { Filter = $"{{ name : '{collectionNamespace.CollectionName}' }}" }; - var currrentCollectionInfo = ListCollections(session, listCollectionOptions, cancellationToken).FirstOrDefault(); - effectiveEncryptedFields = currrentCollectionInfo - ?.GetValue("options", defaultValue: null) - ?.AsBsonDocument - ?.GetValue("encryptedFields", defaultValue: null) - ?.ToBsonDocument(); - } - } - - var messageEncoderSettings = GetMessageEncoderSettings(); - return DropCollectionOperation.CreateEncryptedDropCollectionOperationIfConfigured( - collectionNamespace, - effectiveEncryptedFields, - messageEncoderSettings, - (dco) => - { - dco.WriteConcern = _settings.WriteConcern; - }); - } - - private async Task> CreateDropCollectionOperationAsync(string name, DropCollectionOptions options, IClientSessionHandle session, CancellationToken cancellationToken) + private IWriteOperation CreateDropCollectionOperation(CollectionNamespace collectionNamespace, BsonDocument effectiveEncryptedFields) { - var collectionNamespace = new CollectionNamespace(_databaseNamespace, name); - - options = options ?? new DropCollectionOptions(); - - var encryptedFieldsMap = _client.Settings?.AutoEncryptionOptions?.EncryptedFieldsMap; - if (!EncryptedCollectionHelper.TryGetEffectiveEncryptedFields(collectionNamespace, options.EncryptedFields, encryptedFieldsMap, out var effectiveEncryptedFields)) - { - if (encryptedFieldsMap != null) - { - var listCollectionOptions = new ListCollectionsOptions() { Filter = $"{{ name : '{collectionNamespace.CollectionName}' }}" }; - var currentCollectionsInfo = await ListCollectionsAsync(session, listCollectionOptions, cancellationToken).ConfigureAwait(false); - var currentCollectionInfo = await currentCollectionsInfo.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); - effectiveEncryptedFields = currentCollectionInfo - ?.GetValue("options", defaultValue: null) - ?.AsBsonDocument - ?.GetValue("encryptedFields", defaultValue: null) - ?.ToBsonDocument(); - } - } - var messageEncoderSettings = GetMessageEncoderSettings(); return DropCollectionOperation.CreateEncryptedDropCollectionOperationIfConfigured( collectionNamespace, @@ -778,9 +677,10 @@ private async Task> CreateDropCollectionOperationA }); } - private ListCollectionsOperation CreateListCollectionNamesOperation(ListCollectionNamesOptions options, RenderArgs renderArgs) + private ListCollectionsOperation CreateListCollectionNamesOperation(ListCollectionNamesOptions options) { var messageEncoderSettings = GetMessageEncoderSettings(); + var renderArgs = GetRenderArgs(BsonDocumentSerializer.Instance); return new ListCollectionsOperation(_databaseNamespace, messageEncoderSettings) { AuthorizedCollections = options?.AuthorizedCollections, @@ -791,8 +691,9 @@ private ListCollectionsOperation CreateListCollectionNamesOperation(ListCollecti }; } - private ListCollectionsOperation CreateListCollectionsOperation(ListCollectionsOptions options, RenderArgs renderArgs) + private ListCollectionsOperation CreateListCollectionsOperation(ListCollectionsOptions options) { + var renderArgs = GetRenderArgs(BsonDocumentSerializer.Instance); var messageEncoderSettings = GetMessageEncoderSettings(); return new ListCollectionsOperation(_databaseNamespace, messageEncoderSettings) { @@ -803,23 +704,10 @@ private ListCollectionsOperation CreateListCollectionsOperation(ListCollectionsO }; } - private IReadBinding CreateReadBinding(IClientSessionHandle session, ReadPreference readPreference) - { - if (session.IsInTransaction && readPreference.ReadPreferenceMode != ReadPreferenceMode.Primary) - { - throw new InvalidOperationException("Read preference in a transaction must be primary."); - } - - return ChannelPinningHelper.CreateReadBinding(_cluster, session.WrappedCoreSession.Fork(), readPreference); - } - - private IWriteBindingHandle CreateReadWriteBinding(IClientSessionHandle session) - { - return ChannelPinningHelper.CreateReadWriteBinding(_cluster, session.WrappedCoreSession.Fork()); - } - private RenameCollectionOperation CreateRenameCollectionOperation(string oldName, string newName, RenameCollectionOptions options) { + options ??= new RenameCollectionOptions(); + var messageEncoderSettings = GetMessageEncoderSettings(); return new RenameCollectionOperation( new CollectionNamespace(_databaseNamespace, oldName), @@ -843,9 +731,10 @@ private ReadCommandOperation CreateRunCommandOperation(Command private ChangeStreamOperation CreateChangeStreamOperation( PipelineDefinition, TResult> pipeline, - ChangeStreamOptions options, - ExpressionTranslationOptions translationOptions) + ChangeStreamOptions options) { + var translationOptions = _client.Settings.TranslationOptions; + return ChangeStreamHelper.CreateChangeStreamOperation( this, pipeline, @@ -856,53 +745,68 @@ private ChangeStreamOperation CreateChangeStreamOperation( translationOptions); } - private IEnumerable ExtractCollectionNames(IEnumerable collections) - { - return collections.Select(collection => collection["name"].AsString); - } + private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, CancellationToken cancellationToken) + => ExecuteReadOperation(session, operation, _readOperationOptions, cancellationToken); - private T ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, CancellationToken cancellationToken) - { - var readPreference = ReadPreferenceResolver.GetEffectiveReadPreference(session, null, _settings.ReadPreference); - return ExecuteReadOperation(session, operation, readPreference, cancellationToken); - } + private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, ReadOperationOptions options, CancellationToken cancellationToken) + => _operationExecutor.ExecuteReadOperation(session, operation, options, true, cancellationToken); - private T ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, ReadPreference readPreference, CancellationToken cancellationToken) - { - using (var binding = CreateReadBinding(session, readPreference)) - { - return _operationExecutor.ExecuteReadOperation(binding, operation, cancellationToken); - } - } + private Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, CancellationToken cancellationToken) + => ExecuteReadOperationAsync(session, operation, _readOperationOptions, cancellationToken); - private Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, CancellationToken cancellationToken) - { - var readPreference = ReadPreferenceResolver.GetEffectiveReadPreference(session, null, _settings.ReadPreference); - return ExecuteReadOperationAsync(session, operation, readPreference, cancellationToken); - } + private Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, ReadOperationOptions options, CancellationToken cancellationToken) + => _operationExecutor.ExecuteReadOperationAsync(session, operation, options, true, cancellationToken); + + private TResult ExecuteWriteOperation(IClientSessionHandle session, IWriteOperation operation, CancellationToken cancellationToken) + => _operationExecutor.ExecuteWriteOperation(session, operation, _writeOperationOptions, true, cancellationToken); - private async Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, ReadPreference readPreference, CancellationToken cancellationToken) + private Task ExecuteWriteOperationAsync(IClientSessionHandle session, IWriteOperation operation, CancellationToken cancellationToken) + => _operationExecutor.ExecuteWriteOperationAsync(session, operation, _writeOperationOptions, true, cancellationToken); + + private IEnumerable ExtractCollectionNames(IEnumerable collections) { - using (var binding = CreateReadBinding(session, readPreference)) - { - return await _operationExecutor.ExecuteReadOperationAsync(binding, operation, cancellationToken).ConfigureAwait(false); - } + return collections.Select(collection => collection["name"].AsString); } - private T ExecuteWriteOperation(IClientSessionHandle session, IWriteOperation operation, CancellationToken cancellationToken) + private BsonDocument GetEffectiveEncryptedFields(IClientSessionHandle session, CollectionNamespace collectionNamespace, DropCollectionOptions options, CancellationToken cancellationToken) { - using (var binding = CreateReadWriteBinding(session)) + var encryptedFieldsMap = _client.Settings?.AutoEncryptionOptions?.EncryptedFieldsMap; + if (!EncryptedCollectionHelper.TryGetEffectiveEncryptedFields(collectionNamespace, options?.EncryptedFields, encryptedFieldsMap, out var effectiveEncryptedFields)) { - return _operationExecutor.ExecuteWriteOperation(binding, operation, cancellationToken); + if (encryptedFieldsMap != null) + { + var listCollectionOptions = new ListCollectionsOptions() { Filter = $"{{ name : '{collectionNamespace.CollectionName}' }}" }; + var currentCollectionInfo = ListCollections(session, listCollectionOptions, cancellationToken: cancellationToken).FirstOrDefault(); + effectiveEncryptedFields = currentCollectionInfo + ?.GetValue("options", defaultValue: null) + ?.AsBsonDocument + ?.GetValue("encryptedFields", defaultValue: null) + ?.ToBsonDocument(); + } } + + return effectiveEncryptedFields; } - private async Task ExecuteWriteOperationAsync(IClientSessionHandle session, IWriteOperation operation, CancellationToken cancellationToken) + private async Task GetEffectiveEncryptedFieldsAsync(IClientSessionHandle session, CollectionNamespace collectionNamespace, DropCollectionOptions options, CancellationToken cancellationToken) { - using (var binding = CreateReadWriteBinding(session)) + var encryptedFieldsMap = _client.Settings?.AutoEncryptionOptions?.EncryptedFieldsMap; + if (!EncryptedCollectionHelper.TryGetEffectiveEncryptedFields(collectionNamespace, options?.EncryptedFields, encryptedFieldsMap, out var effectiveEncryptedFields)) { - return await _operationExecutor.ExecuteWriteOperationAsync(binding, operation, cancellationToken).ConfigureAwait(false); + if (encryptedFieldsMap != null) + { + var listCollectionOptions = new ListCollectionsOptions() { Filter = $"{{ name : '{collectionNamespace.CollectionName}' }}" }; + var currentCollectionsInfo = await ListCollectionsAsync(session, listCollectionOptions, cancellationToken: cancellationToken).ConfigureAwait(false); + var currentCollectionInfo = await currentCollectionsInfo.FirstOrDefaultAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + effectiveEncryptedFields = currentCollectionInfo + ?.GetValue("options", defaultValue: null) + ?.AsBsonDocument + ?.GetValue("encryptedFields", defaultValue: null) + ?.ToBsonDocument(); + } } + + return effectiveEncryptedFields; } private MessageEncoderSettings GetMessageEncoderSettings() @@ -932,37 +836,5 @@ private RenderArgs GetRenderArgs(IBsonSerializer(documentSerializer, _settings.SerializerRegistry, translationOptions: translationOptions); } - - private void UsingImplicitSession(Action func, CancellationToken cancellationToken) - { - using (var session = _operationExecutor.StartImplicitSession(cancellationToken)) - { - func(session); - } - } - - private TResult UsingImplicitSession(Func func, CancellationToken cancellationToken) - { - using (var session = _operationExecutor.StartImplicitSession(cancellationToken)) - { - return func(session); - } - } - - private async Task UsingImplicitSessionAsync(Func funcAsync, CancellationToken cancellationToken) - { - using (var session = await _operationExecutor.StartImplicitSessionAsync(cancellationToken).ConfigureAwait(false)) - { - await funcAsync(session).ConfigureAwait(false); - } - } - - private async Task UsingImplicitSessionAsync(Func> funcAsync, CancellationToken cancellationToken) - { - using (var session = await _operationExecutor.StartImplicitSessionAsync(cancellationToken).ConfigureAwait(false)) - { - return await funcAsync(session).ConfigureAwait(false); - } - } } } diff --git a/src/MongoDB.Driver/OperationExecutor.cs b/src/MongoDB.Driver/OperationExecutor.cs index cc521cdf762..295d136bd09 100644 --- a/src/MongoDB.Driver/OperationExecutor.cs +++ b/src/MongoDB.Driver/OperationExecutor.cs @@ -13,50 +13,138 @@ * limitations under the License. */ +using System; using System.Threading; using System.Threading.Tasks; +using MongoDB.Driver.Core; using MongoDB.Driver.Core.Bindings; +using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Operations; namespace MongoDB.Driver { internal sealed class OperationExecutor : IOperationExecutor { - private readonly MongoClient _client; + private readonly IMongoClient _client; + private bool _isDisposed; - public OperationExecutor(MongoClient client) + public OperationExecutor(IMongoClient client) { _client = client; } - public TResult ExecuteReadOperation(IReadBinding binding, IReadOperation operation, CancellationToken cancellationToken) + public void Dispose() { + _isDisposed = true; + } + + public TResult ExecuteReadOperation( + IClientSessionHandle session, + IReadOperation operation, + ReadOperationOptions options, + bool allowChannelPinning, + CancellationToken cancellationToken) + { + Ensure.IsNotNull(operation, nameof(operation)); + Ensure.IsNotNull(options, nameof(options)); + Ensure.IsNotNull(session, nameof(session)); + ThrowIfDisposed(); + + var readPreference = options.GetEffectiveReadPreference(session); + using var binding = CreateReadBinding(session, readPreference, allowChannelPinning); return operation.Execute(binding, cancellationToken); } - public async Task ExecuteReadOperationAsync(IReadBinding binding, IReadOperation operation, CancellationToken cancellationToken) + public async Task ExecuteReadOperationAsync( + IClientSessionHandle session, + IReadOperation operation, + ReadOperationOptions options, + bool allowChannelPinning, + CancellationToken cancellationToken) { + Ensure.IsNotNull(operation, nameof(operation)); + Ensure.IsNotNull(options, nameof(options)); + Ensure.IsNotNull(session, nameof(session)); + ThrowIfDisposed(); + + var readPreference = options.GetEffectiveReadPreference(session); + using var binding = CreateReadBinding(session, readPreference, allowChannelPinning); return await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); } - public TResult ExecuteWriteOperation(IWriteBinding binding, IWriteOperation operation, CancellationToken cancellationToken) + public TResult ExecuteWriteOperation( + IClientSessionHandle session, + IWriteOperation operation, + WriteOperationOptions options, + bool allowChannelPinning, + CancellationToken cancellationToken) { + Ensure.IsNotNull(operation, nameof(operation)); + Ensure.IsNotNull(options, nameof(options)); + Ensure.IsNotNull(session, nameof(session)); + ThrowIfDisposed(); + + using var binding = CreateReadWriteBinding(session, allowChannelPinning); return operation.Execute(binding, cancellationToken); } - public async Task ExecuteWriteOperationAsync(IWriteBinding binding, IWriteOperation operation, CancellationToken cancellationToken) + public async Task ExecuteWriteOperationAsync( + IClientSessionHandle session, + IWriteOperation operation, + WriteOperationOptions options, + bool allowChannelPinning, + CancellationToken cancellationToken) { + Ensure.IsNotNull(operation, nameof(operation)); + Ensure.IsNotNull(options, nameof(options)); + Ensure.IsNotNull(session, nameof(session)); + ThrowIfDisposed(); + + using var binding = CreateReadWriteBinding(session, allowChannelPinning); return await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); } - public IClientSessionHandle StartImplicitSession(CancellationToken cancellationToken) + public IClientSessionHandle StartImplicitSession() + { + ThrowIfDisposed(); + var options = new ClientSessionOptions { CausalConsistency = false, Snapshot = false }; + var coreSession = _client.GetClusterInternal().StartSession(options.ToCore(isImplicit: true)); + return new ClientSessionHandle(_client, options, coreSession); + } + + private IReadBindingHandle CreateReadBinding(IClientSessionHandle session, ReadPreference readPreference, bool allowChannelPinning) + { + if (session.IsInTransaction && readPreference.ReadPreferenceMode != ReadPreferenceMode.Primary) + { + throw new InvalidOperationException("Read preference in a transaction must be primary."); + } + + if (allowChannelPinning) + { + return ChannelPinningHelper.CreateReadBinding(_client.GetClusterInternal(), session.WrappedCoreSession.Fork(), readPreference); + } + + var binding = new ReadPreferenceBinding(_client.GetClusterInternal(), readPreference, session.WrappedCoreSession.Fork()); + return new ReadBindingHandle(binding); + } + + private IReadWriteBindingHandle CreateReadWriteBinding(IClientSessionHandle session, bool allowChannelPinning) { - return _client.StartImplicitSession(cancellationToken); + if (allowChannelPinning) + { + return ChannelPinningHelper.CreateReadWriteBinding(_client.GetClusterInternal(), session.WrappedCoreSession.Fork()); + } + + var binding = new WritableServerBinding(_client.GetClusterInternal(), session.WrappedCoreSession.Fork()); + return new ReadWriteBindingHandle(binding); } - public Task StartImplicitSessionAsync(CancellationToken cancellationToken) + private void ThrowIfDisposed() { - return _client.StartImplicitSessionAsync(cancellationToken); + if (_isDisposed) + { + throw new ObjectDisposedException(nameof(OperationExecutor)); + } } } } diff --git a/src/MongoDB.Driver/ReadOperationOptions.cs b/src/MongoDB.Driver/ReadOperationOptions.cs new file mode 100644 index 00000000000..2473d5dc045 --- /dev/null +++ b/src/MongoDB.Driver/ReadOperationOptions.cs @@ -0,0 +1,42 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace MongoDB.Driver +{ + internal record ReadOperationOptions(ReadPreference ExplicitReadPreference = null, ReadPreference DefaultReadPreference = null); + + internal static class ReadOperationOptionsExtensions + { + public static ReadPreference GetEffectiveReadPreference(this ReadOperationOptions options, IClientSessionHandle session) + { + if (options.ExplicitReadPreference != null) + { + return options.ExplicitReadPreference; + } + + if (session.IsInTransaction) + { + var transactionReadPreference = session.WrappedCoreSession.CurrentTransaction.TransactionOptions.ReadPreference; + if (transactionReadPreference != null) + { + return transactionReadPreference; + } + } + + return options.DefaultReadPreference ?? ReadPreference.Primary; + } + } +} + diff --git a/src/MongoDB.Driver/ReadPreferenceResolver.cs b/src/MongoDB.Driver/ReadPreferenceResolver.cs deleted file mode 100644 index 6fbc9ed27f8..00000000000 --- a/src/MongoDB.Driver/ReadPreferenceResolver.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright 2018-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -namespace MongoDB.Driver -{ - internal static class ReadPreferenceResolver - { - public static ReadPreference GetEffectiveReadPreference( - IClientSessionHandle session, - ReadPreference explicitReadPreference, - ReadPreference defaultReadPreference) - { - if (explicitReadPreference != null) - { - return explicitReadPreference; - } - - if (session.IsInTransaction) - { - var transactionReadPreference = session.WrappedCoreSession.CurrentTransaction.TransactionOptions.ReadPreference; - if (transactionReadPreference != null) - { - return transactionReadPreference; - } - } - - return defaultReadPreference ?? ReadPreference.Primary; - } - } -} diff --git a/src/MongoDB.Driver/WriteOperationOptions.cs b/src/MongoDB.Driver/WriteOperationOptions.cs new file mode 100644 index 00000000000..fd56dc0fa78 --- /dev/null +++ b/src/MongoDB.Driver/WriteOperationOptions.cs @@ -0,0 +1,20 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace MongoDB.Driver +{ + internal record WriteOperationOptions(); +} + diff --git a/tests/MongoDB.Driver.Tests/MockOperationExecutor.cs b/tests/MongoDB.Driver.Tests/MockOperationExecutor.cs index 6c9c18b6410..6abf665a10e 100644 --- a/tests/MongoDB.Driver.Tests/MockOperationExecutor.cs +++ b/tests/MongoDB.Driver.Tests/MockOperationExecutor.cs @@ -48,6 +48,10 @@ public int QueuedCallCount get { return _calls.Count; } } + public void Dispose() + { + } + public void EnqueueResult(TResult result) { _results.Enqueue(result); @@ -58,15 +62,20 @@ public void EnqueueException(Exception exception) _results.Enqueue(exception); } - public TResult ExecuteReadOperation(IReadBinding binding, IReadOperation operation, CancellationToken cancellationToken) + public TResult ExecuteReadOperation( + IClientSessionHandle session, + IReadOperation operation, + ReadOperationOptions readOperationOptions, + bool allowChannelPinning, + CancellationToken cancellationToken) { _calls.Enqueue(new ReadCall { - Binding = binding, Operation = operation, CancellationToken = cancellationToken, - SessionId = binding.Session.Id, - UsedImplicitSession = binding.Session.IsImplicit + Options = readOperationOptions, + SessionId = session?.WrappedCoreSession.Id, + UsedImplicitSession = session == null || session.IsImplicit }); if (_results.Count > 0) @@ -85,11 +94,16 @@ public TResult ExecuteReadOperation(IReadBinding binding, IReadOperatio return default(TResult); } - public Task ExecuteReadOperationAsync(IReadBinding binding, IReadOperation operation, CancellationToken cancellationToken) + public Task ExecuteReadOperationAsync( + IClientSessionHandle session, + IReadOperation operation, + ReadOperationOptions readOperationOptions, + bool allowChannelPinning, + CancellationToken cancellationToken) { try { - var result = ExecuteReadOperation(binding, operation, cancellationToken); + var result = ExecuteReadOperation(session, operation, readOperationOptions, allowChannelPinning, cancellationToken); return Task.FromResult(result); } catch (Exception ex) @@ -100,15 +114,20 @@ public Task ExecuteReadOperationAsync(IReadBinding binding, IR } } - public TResult ExecuteWriteOperation(IWriteBinding binding, IWriteOperation operation, CancellationToken cancellationToken) + public TResult ExecuteWriteOperation( + IClientSessionHandle session, + IWriteOperation operation, + WriteOperationOptions writeOperationOptions, + bool allowChannelPinning, + CancellationToken cancellationToken) { _calls.Enqueue(new WriteCall { - Binding = binding, Operation = operation, CancellationToken = cancellationToken, - SessionId = binding.Session.Id, - UsedImplicitSession = binding.Session.IsImplicit + Options = writeOperationOptions, + SessionId = session?.WrappedCoreSession.Id, + UsedImplicitSession = session == null || session.IsImplicit }); if (_results.Count > 0) @@ -127,11 +146,16 @@ public TResult ExecuteWriteOperation(IWriteBinding binding, IWriteOpera return default(TResult); } - public Task ExecuteWriteOperationAsync(IWriteBinding binding, IWriteOperation operation, CancellationToken cancellationToken) + public Task ExecuteWriteOperationAsync( + IClientSessionHandle session, + IWriteOperation operation, + WriteOperationOptions writeOperationOptions, + bool allowChannelPinning, + CancellationToken cancellationToken) { try { - var result = ExecuteWriteOperation(binding, operation, cancellationToken); + var result = ExecuteWriteOperation(session, operation, writeOperationOptions, allowChannelPinning, cancellationToken); return Task.FromResult(result); } catch (Exception ex) @@ -176,7 +200,7 @@ public WriteCall GetWriteCall() return writeCall; } - public IClientSessionHandle StartImplicitSession(CancellationToken cancellationToken) + public IClientSessionHandle StartImplicitSession() { var cluster = Mock.Of(); var options = new ClientSessionOptions(); @@ -186,25 +210,20 @@ public IClientSessionHandle StartImplicitSession(CancellationToken cancellationT return new ClientSessionHandle(_client, options, coreSessionHandle); } - public Task StartImplicitSessionAsync(CancellationToken cancellationToken) - { - return Task.FromResult(StartImplicitSession(cancellationToken)); - } - public class ReadCall { - public IReadBinding Binding { get; set; } public IReadOperation Operation { get; set; } public CancellationToken CancellationToken { get; set; } + public ReadOperationOptions Options { get; set; } public BsonDocument SessionId { get; set; } public bool UsedImplicitSession { get; set; } } public class WriteCall { - public IWriteBinding Binding { get; set; } public IWriteOperation Operation { get; set; } public CancellationToken CancellationToken { get; set; } + public WriteOperationOptions Options { get; set; } public BsonDocument SessionId { get; set; } public bool UsedImplicitSession { get; set; } } diff --git a/tests/MongoDB.Driver.Tests/MongoClientTests.cs b/tests/MongoDB.Driver.Tests/MongoClientTests.cs index 36d6bca9231..026f5b83cd4 100644 --- a/tests/MongoDB.Driver.Tests/MongoClientTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoClientTests.cs @@ -119,9 +119,6 @@ public void Disposed_client_should_throw_on_member_access() var exception = Record.Exception(() => client.Cluster); exception.Should().BeOfType(); - - exception = Record.Exception(() => client.StartImplicitSession(default)); - exception.Should().BeOfType(); } [Theory] @@ -132,7 +129,7 @@ public void DropDatabase_should_invoke_the_correct_operation( { var operationExecutor = new MockOperationExecutor(); var writeConcern = new WriteConcern(1); - var subject = new MongoClient(operationExecutor, DriverTestConfiguration.GetClientSettings()).WithWriteConcern(writeConcern); + var subject = new MongoClient(DriverTestConfiguration.GetClientSettings(), _ => operationExecutor).WithWriteConcern(writeConcern); var session = CreateClientSession(); using var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; @@ -183,7 +180,7 @@ public void ListDatabaseNames_should_invoke_the_correct_operation( [Values(false, true)] bool async) { var operationExecutor = new MockOperationExecutor(); - var subject = new MongoClient(operationExecutor, DriverTestConfiguration.GetClientSettings()); + var subject = new MongoClient(DriverTestConfiguration.GetClientSettings(), _ => operationExecutor); var session = CreateClientSession(); using var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; @@ -277,7 +274,7 @@ public void ListDatabases_should_invoke_the_correct_operation( [Values(false, true)] bool async) { var operationExecutor = new MockOperationExecutor(); - var subject = new MongoClient(operationExecutor, DriverTestConfiguration.GetClientSettings()); + var subject = new MongoClient(DriverTestConfiguration.GetClientSettings(), _ => operationExecutor); var session = CreateClientSession(); using var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; @@ -337,7 +334,7 @@ public void Watch_should_invoke_the_correct_operation( { var operationExecutor = new MockOperationExecutor(); var clientSettings = DriverTestConfiguration.GetClientSettings(); - var subject = new MongoClient(operationExecutor, clientSettings); + var subject = new MongoClient(clientSettings, _ => operationExecutor); var session = usingSession ? CreateClientSession() : null; var pipeline = new EmptyPipelineDefinition>().Limit(1); var options = new ChangeStreamOptions diff --git a/tests/MongoDB.Driver.Tests/MongoDatabasTests.cs b/tests/MongoDB.Driver.Tests/MongoDatabaseTests.cs similarity index 98% rename from tests/MongoDB.Driver.Tests/MongoDatabasTests.cs rename to tests/MongoDB.Driver.Tests/MongoDatabaseTests.cs index 68cb5526beb..c2fc8f9166b 100644 --- a/tests/MongoDB.Driver.Tests/MongoDatabasTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoDatabaseTests.cs @@ -1097,8 +1097,8 @@ public void RunCommand_should_default_to_ReadPreference_primary( var call = _operationExecutor.GetReadCall(); VerifySessionAndCancellationToken(call, session, cancellationToken); - var binding = call.Binding.Should().BeOfType().Subject; - binding.ReadPreference.Should().Be(ReadPreference.Primary); + call.Options.ExplicitReadPreference.Should().BeNull(); + call.Options.DefaultReadPreference.Should().Be(ReadPreference.Primary); var op = call.Operation.Should().BeOfType>().Subject; op.DatabaseNamespace.Should().Be(_subject.DatabaseNamespace); @@ -1144,8 +1144,8 @@ public void RunCommand_should_use_the_provided_ReadPreference( var call = _operationExecutor.GetReadCall(); VerifySessionAndCancellationToken(call, session, cancellationToken); - var binding = call.Binding.Should().BeOfType().Subject; - binding.ReadPreference.Should().Be(readPreference); + call.Options.ExplicitReadPreference.Should().Be(readPreference); + call.Options.DefaultReadPreference.Should().Be(ReadPreference.Primary); var op = call.Operation.Should().BeOfType>().Subject; op.DatabaseNamespace.Should().Be(_subject.DatabaseNamespace); @@ -1190,8 +1190,8 @@ public void RunCommand_should_run_a_non_read_command( var call = _operationExecutor.GetReadCall(); VerifySessionAndCancellationToken(call, session, cancellationToken); - var binding = call.Binding.Should().BeOfType().Subject; - binding.ReadPreference.Should().Be(ReadPreference.Primary); + call.Options.ExplicitReadPreference.Should().BeNull(); + call.Options.DefaultReadPreference.Should().Be(ReadPreference.Primary); var op = call.Operation.Should().BeOfType>().Subject; op.DatabaseNamespace.Should().Be(_subject.DatabaseNamespace); @@ -1236,8 +1236,8 @@ public void RunCommand_should_run_a_json_command( var call = _operationExecutor.GetReadCall(); VerifySessionAndCancellationToken(call, session, cancellationToken); - var binding = call.Binding.Should().BeOfType().Subject; - binding.ReadPreference.Should().Be(ReadPreference.Primary); + call.Options.ExplicitReadPreference.Should().BeNull(); + call.Options.DefaultReadPreference.Should().Be(ReadPreference.Primary); var op = call.Operation.Should().BeOfType>().Subject; op.DatabaseNamespace.Should().Be(_subject.DatabaseNamespace); @@ -1282,8 +1282,8 @@ public void RunCommand_should_run_a_serialized_command( var call = _operationExecutor.GetReadCall(); VerifySessionAndCancellationToken(call, session, cancellationToken); - var binding = call.Binding.Should().BeOfType().Subject; - binding.ReadPreference.Should().Be(ReadPreference.Primary); + call.Options.ExplicitReadPreference.Should().BeNull(); + call.Options.DefaultReadPreference.Should().Be(ReadPreference.Primary); var op = call.Operation.Should().BeOfType>().Subject; op.DatabaseNamespace.Should().Be(_subject.DatabaseNamespace); diff --git a/tests/MongoDB.Driver.Tests/OperationExecutorTests.cs b/tests/MongoDB.Driver.Tests/OperationExecutorTests.cs new file mode 100644 index 00000000000..ef207d784f6 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/OperationExecutorTests.cs @@ -0,0 +1,156 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using MongoDB.Driver.Core.Bindings; +using MongoDB.Driver.Core.Clusters; +using MongoDB.Driver.Core.Operations; +using MongoDB.TestHelpers.XunitExtensions; +using Moq; +using Xunit; + +namespace MongoDB.Driver.Tests +{ + public class OperationExecutorTests + { + [Fact] + public void StartImplicitSession_should_call_cluster_StartSession() + { + var subject = CreateSubject(out var clusterMock, out _); + + subject.StartImplicitSession(); + + clusterMock.Verify(c => c.StartSession(It.Is(v => v.IsImplicit && v.IsCausallyConsistent == false && v.IsSnapshot == false))); + } + + [Theory] + [ParameterAttributeData] + public async Task ExecuteReadOperation_throws_on_null_operation([Values(true, false)] bool async) + { + var subject = CreateSubject(out _, out _); + var options = new ReadOperationOptions(); + var session = Mock.Of(); + + var exception = async ? + await Record.ExceptionAsync(() => subject.ExecuteReadOperationAsync(session, null, options, true, CancellationToken.None)) : + Record.Exception(() => subject.ExecuteReadOperation(session, null, options, true, CancellationToken.None)); + + exception.Should().BeOfType() + .Subject.ParamName.Should().Be("operation"); + } + + [Theory] + [ParameterAttributeData] + public async Task ExecuteReadOperation_throws_on_null_options([Values(true, false)] bool async) + { + var subject = CreateSubject(out _, out _); + var operation = Mock.Of>(); + var session = Mock.Of(); + + var exception = async ? + await Record.ExceptionAsync(() => subject.ExecuteReadOperationAsync(session, operation, null, true, CancellationToken.None)) : + Record.Exception(() => subject.ExecuteReadOperation(session, operation, null, true, CancellationToken.None)); + + exception.Should().BeOfType() + .Subject.ParamName.Should().Be("options"); + } + + [Theory] + [ParameterAttributeData] + public async Task ExecuteReadOperation_throws_on_null_session([Values(true, false)] bool async) + { + var subject = CreateSubject(out _, out _); + var operation = Mock.Of>(); + var options = new ReadOperationOptions(); + + var exception = async ? + await Record.ExceptionAsync(() => subject.ExecuteReadOperationAsync(null, operation, options, true, CancellationToken.None)) : + Record.Exception(() => subject.ExecuteReadOperation(null, operation, options, true, CancellationToken.None)); + + exception.Should().BeOfType() + .Subject.ParamName.Should().Be("session"); + } + + [Theory] + [ParameterAttributeData] + public async Task ExecuteWriteOperation_throws_on_null_operation([Values(true, false)] bool async) + { + var subject = CreateSubject(out _, out _); + var options = new WriteOperationOptions(); + var session = Mock.Of(); + + var exception = async ? + await Record.ExceptionAsync(() => subject.ExecuteWriteOperationAsync(session, null, options, true, CancellationToken.None)) : + Record.Exception(() => subject.ExecuteWriteOperation(session, null, options, true, CancellationToken.None)); + + exception.Should().BeOfType() + .Subject.ParamName.Should().Be("operation"); + } + + [Theory] + [ParameterAttributeData] + public async Task ExecuteWriteOperation_throws_on_null_options([Values(true, false)] bool async) + { + var subject = CreateSubject(out _, out _); + var operation = Mock.Of>(); + var session = Mock.Of(); + + var exception = async ? + await Record.ExceptionAsync(() => subject.ExecuteWriteOperationAsync(session, operation, null, true, CancellationToken.None)) : + Record.Exception(() => subject.ExecuteWriteOperation(session, operation, null, true, CancellationToken.None)); + + exception.Should().BeOfType() + .Subject.ParamName.Should().Be("options"); + } + + [Theory] + [ParameterAttributeData] + public async Task ExecuteWriteOperation_throws_on_null_session([Values(true, false)] bool async) + { + var subject = CreateSubject(out _, out _); + var operation = Mock.Of>(); + var options = new WriteOperationOptions(); + + var exception = async ? + await Record.ExceptionAsync(() => subject.ExecuteWriteOperationAsync(null, operation, options, true, CancellationToken.None)) : + Record.Exception(() => subject.ExecuteWriteOperation(null, operation, options, true, CancellationToken.None)); + + exception.Should().BeOfType() + .Subject.ParamName.Should().Be("session"); + } + + private OperationExecutor CreateSubject(out Mock clusterMock, out Mock implicitSessionMock) + { + implicitSessionMock = CreateCoreSessionMock(true); + clusterMock = new Mock(); + clusterMock.Setup(c => c.StartSession(It.IsAny())).Returns(implicitSessionMock.Object); + var clientMock = new Mock(); + clientMock.SetupGet(c => c.Cluster).Returns(clusterMock.Object); + return new OperationExecutor(clientMock.Object); + } + + private static Mock CreateCoreSessionMock(bool isImplicit) + { + var sessionMock = new Mock(); + sessionMock.SetupGet(s => s.IsImplicit).Returns(isImplicit); + sessionMock.Setup(s => s.Fork()).Returns(() => CreateCoreSessionMock(isImplicit).Object); + return sessionMock; + } + } +} + diff --git a/tests/MongoDB.Driver.Tests/ReadOperationOptionsTests.cs b/tests/MongoDB.Driver.Tests/ReadOperationOptionsTests.cs new file mode 100644 index 00000000000..585b2e442ff --- /dev/null +++ b/tests/MongoDB.Driver.Tests/ReadOperationOptionsTests.cs @@ -0,0 +1,70 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Collections.Generic; +using FluentAssertions; +using MongoDB.Driver.Core.Bindings; +using Moq; +using Xunit; + +namespace MongoDB.Driver.Tests +{ + public class ReadOperationOptionsTests + { + [Theory] + [MemberData(nameof(GetEffectiveReadPreferenceTestCases))] + public void GetEffectiveReadPreferenceTests( + ReadPreference expectedReadPreference, + ReadPreference explicitReadPreference, + ReadPreference defaultReadPreference, + IClientSessionHandle session) + { + var readOperationOptions = new ReadOperationOptions(explicitReadPreference, defaultReadPreference); + var result = readOperationOptions.GetEffectiveReadPreference(session); + + result.Should().Be(expectedReadPreference); + } + + public static IEnumerable GetEffectiveReadPreferenceTestCases() + { + var noTransactionSession = CreateSessionMock(null); + var inTransactionSession = CreateSessionMock(new TransactionOptions(readPreference: ReadPreference.Nearest)); + var inTransactionNoPreferenceSession = CreateSessionMock(new TransactionOptions()); + + yield return [ReadPreference.Primary, null, null, noTransactionSession]; + yield return [ReadPreference.Secondary, ReadPreference.Secondary, ReadPreference.SecondaryPreferred, noTransactionSession]; + yield return [ReadPreference.Secondary, ReadPreference.Secondary, ReadPreference.SecondaryPreferred, inTransactionSession]; + yield return [ReadPreference.SecondaryPreferred, null, ReadPreference.SecondaryPreferred, noTransactionSession]; + yield return [ReadPreference.Nearest, null, ReadPreference.SecondaryPreferred, inTransactionSession]; + yield return [ReadPreference.Primary, null, null, inTransactionNoPreferenceSession]; + yield return [ReadPreference.SecondaryPreferred, null, ReadPreference.SecondaryPreferred, inTransactionNoPreferenceSession]; + } + + private static IClientSessionHandle CreateSessionMock(TransactionOptions transactionOptions) + { + var sessionMock = new Mock(); + if (transactionOptions != null) + { + sessionMock.SetupGet(s => s.IsInTransaction).Returns(true); + var coreSessionMock = new Mock(); + coreSessionMock.SetupGet(s => s.CurrentTransaction).Returns(new CoreTransaction(0, transactionOptions)); + sessionMock.SetupGet(s => s.WrappedCoreSession).Returns(coreSessionMock.Object); + } + + return sessionMock.Object; + } + } +} + From e62da2b9013aefa57022931232fef6ba621186d2 Mon Sep 17 00:00:00 2001 From: Medha Tiwari <75640645+medhatiwari@users.noreply.github.com> Date: Fri, 6 Jun 2025 04:55:00 +0530 Subject: [PATCH 16/24] CSHARP-5603: Add Big Endian support in BinaryVectorReader and BinaryVectorWriter (#1682) --- src/MongoDB.Bson/IO/BinaryPrimitivesCompat.cs | 51 +++++++++++ .../Serialization/BinaryVectorReader.cs | 42 +++++---- .../Serialization/BinaryVectorWriter.cs | 37 ++++++-- .../IO/BinaryPrimitivesCompatTests.cs | 89 +++++++++++++++++++ .../BinaryVectorSerializerTests.cs | 35 +++++++- 5 files changed, 229 insertions(+), 25 deletions(-) create mode 100644 tests/MongoDB.Bson.Tests/IO/BinaryPrimitivesCompatTests.cs diff --git a/src/MongoDB.Bson/IO/BinaryPrimitivesCompat.cs b/src/MongoDB.Bson/IO/BinaryPrimitivesCompat.cs index 323649f6d08..41f3f90b67a 100644 --- a/src/MongoDB.Bson/IO/BinaryPrimitivesCompat.cs +++ b/src/MongoDB.Bson/IO/BinaryPrimitivesCompat.cs @@ -15,6 +15,7 @@ using System; using System.Buffers.Binary; +using System.Runtime.InteropServices; namespace MongoDB.Bson.IO { @@ -31,5 +32,55 @@ public static void WriteDoubleLittleEndian(Span destination, double value) { BinaryPrimitives.WriteInt64LittleEndian(destination, BitConverter.DoubleToInt64Bits(value)); } + + public static float ReadSingleLittleEndian(ReadOnlySpan source) + { + if (source.Length < 4) + { + throw new ArgumentOutOfRangeException(nameof(source.Length), "Source span is too small to contain a float."); + } + +#if NET6_0_OR_GREATER + return BinaryPrimitives.ReadSingleLittleEndian(source); +#else + // Constructs a 32-bit float from 4 Little Endian bytes in a platform-agnostic way. + // Ensures correct bit pattern regardless of system endianness. + int intValue = + source[0] | + (source[1] << 8) | + (source[2] << 16) | + (source[3] << 24); + + // This struct emulates BitConverter.Int32BitsToSingle for platforms like net472. + return new FloatIntUnion { IntValue = intValue }.FloatValue; +#endif + } + + public static void WriteSingleLittleEndian(Span destination, float value) + { + if (destination.Length < 4) + { + throw new ArgumentOutOfRangeException(nameof(destination.Length), "Destination span is too small to hold a float."); + } + +#if NET6_0_OR_GREATER + BinaryPrimitives.WriteSingleLittleEndian(destination, value); +#else + // This struct emulates BitConverter.SingleToInt32Bits for platforms like net472. + int intValue = new FloatIntUnion { FloatValue = value }.IntValue; + + destination[0] = (byte)(intValue); + destination[1] = (byte)(intValue >> 8); + destination[2] = (byte)(intValue >> 16); + destination[3] = (byte)(intValue >> 24); +#endif + } + + [StructLayout(LayoutKind.Explicit)] + private struct FloatIntUnion + { + [FieldOffset(0)] public float FloatValue; + [FieldOffset(0)] public int IntValue; + } } } diff --git a/src/MongoDB.Bson/Serialization/BinaryVectorReader.cs b/src/MongoDB.Bson/Serialization/BinaryVectorReader.cs index ef83c201091..3b751f3c9d6 100644 --- a/src/MongoDB.Bson/Serialization/BinaryVectorReader.cs +++ b/src/MongoDB.Bson/Serialization/BinaryVectorReader.cs @@ -17,6 +17,7 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using MongoDB.Bson.IO; namespace MongoDB.Bson.Serialization { @@ -41,21 +42,8 @@ public static (TItem[] Items, byte Padding, BinaryVectorDataType VectorDataType) switch (vectorDataType) { case BinaryVectorDataType.Float32: - - if ((vectorDataBytes.Span.Length & 3) != 0) - { - throw new FormatException("Data length of binary vector of type Float32 must be a multiple of 4 bytes."); - } - - if (BitConverter.IsLittleEndian) - { - var singles = MemoryMarshal.Cast(vectorDataBytes.Span); - items = (TItem[])(object)singles.ToArray(); - } - else - { - throw new NotSupportedException("Binary vector data is not supported on Big Endian architecture yet."); - } + var floatArray = ReadSinglesArrayLittleEndian(vectorDataBytes.Span); + items = (TItem[])(object)floatArray; break; case BinaryVectorDataType.Int8: var itemsSpan = MemoryMarshal.Cast(vectorDataBytes.Span); @@ -123,6 +111,30 @@ TExpectedItem[] AsTypedArrayOrThrow() return result; } } + + private static float[] ReadSinglesArrayLittleEndian(ReadOnlySpan span) + { + if ((span.Length & 3) != 0) + { + throw new FormatException("Data length of binary vector of type Float32 must be a multiple of 4 bytes."); + } + + float[] result; + if (BitConverter.IsLittleEndian) + { + result = MemoryMarshal.Cast(span).ToArray(); + } + else + { + var count = span.Length / 4; + result = new float[count]; + for (int i = 0; i < count; i++) + { + result[i] = BinaryPrimitivesCompat.ReadSingleLittleEndian(span.Slice(i * 4, 4)); + } + } + return result; + } public static void ValidateItemType(BinaryVectorDataType binaryVectorDataType) { diff --git a/src/MongoDB.Bson/Serialization/BinaryVectorWriter.cs b/src/MongoDB.Bson/Serialization/BinaryVectorWriter.cs index 0e9d5e74f6d..9f1ba73b075 100644 --- a/src/MongoDB.Bson/Serialization/BinaryVectorWriter.cs +++ b/src/MongoDB.Bson/Serialization/BinaryVectorWriter.cs @@ -15,6 +15,7 @@ using System; using System.Runtime.InteropServices; +using MongoDB.Bson.IO; namespace MongoDB.Bson.Serialization { @@ -35,15 +36,39 @@ public static byte[] WriteToBytes(BinaryVector binaryVector) public static byte[] WriteToBytes(ReadOnlySpan vectorData, BinaryVectorDataType binaryVectorDataType, byte padding) where TItem : struct { - if (!BitConverter.IsLittleEndian) + switch (binaryVectorDataType) { - throw new NotSupportedException("Binary vector data is not supported on Big Endian architecture yet."); - } + case BinaryVectorDataType.Float32: + var length = vectorData.Length * 4; + var result = new byte[2 + length]; + result[0] = (byte)binaryVectorDataType; + result[1] = padding; + + var floatSpan = MemoryMarshal.Cast(vectorData); + var floatOutput = result.AsSpan(2); + + if (BitConverter.IsLittleEndian) + { + MemoryMarshal.Cast(floatSpan).CopyTo(floatOutput); + } + else + { + for (int i = 0; i < floatSpan.Length; i++) + { + BinaryPrimitivesCompat.WriteSingleLittleEndian(floatOutput.Slice(i * 4, 4), floatSpan[i]); + } + } - var vectorDataBytes = MemoryMarshal.Cast(vectorData); - byte[] result = [(byte)binaryVectorDataType, padding, .. vectorDataBytes]; + return result; - return result; + case BinaryVectorDataType.Int8: + case BinaryVectorDataType.PackedBit: + var vectorDataBytes = MemoryMarshal.Cast(vectorData); + return [(byte)binaryVectorDataType, padding, .. vectorDataBytes]; + + default: + throw new NotSupportedException($"Binary vector serialization is not supported for {binaryVectorDataType}."); + } } } } diff --git a/tests/MongoDB.Bson.Tests/IO/BinaryPrimitivesCompatTests.cs b/tests/MongoDB.Bson.Tests/IO/BinaryPrimitivesCompatTests.cs new file mode 100644 index 00000000000..50500f314d3 --- /dev/null +++ b/tests/MongoDB.Bson.Tests/IO/BinaryPrimitivesCompatTests.cs @@ -0,0 +1,89 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using Xunit; +using FluentAssertions; +using MongoDB.Bson.IO; + +namespace MongoDB.Bson.Tests.IO +{ + public class BinaryPrimitivesCompatTests + { + [Fact] + public void ReadSingleLittleEndian_should_read_correctly() + { + var bytes = new byte[] { 0x00, 0x00, 0x80, 0x3F }; // 1.0f in little endian + var result = BinaryPrimitivesCompat.ReadSingleLittleEndian(bytes); + result.Should().Be(1.0f); + } + + [Fact] + public void ReadSingleLittleEndian_should_throw_on_insufficient_length() + { + var shortBuffer = new byte[3]; + var exception = Record.Exception(() => + BinaryPrimitivesCompat.ReadSingleLittleEndian(shortBuffer)); + + var e = exception.Should().BeOfType().Subject; + e.ParamName.Should().Be("Length"); + } + + [Fact] + public void WriteSingleLittleEndian_should_throw_on_insufficient_length() + { + var shortBuffer = new byte[3]; + var exception = Record.Exception(() => + BinaryPrimitivesCompat.WriteSingleLittleEndian(shortBuffer, 1.23f)); + + var e = exception.Should().BeOfType().Subject; + e.ParamName.Should().Be("Length"); + } + + [Fact] + public void WriteSingleLittleEndian_should_write_correctly() + { + Span buffer = new byte[4]; + BinaryPrimitivesCompat.WriteSingleLittleEndian(buffer, 1.0f); + buffer.ToArray().Should().Equal(0x00, 0x00, 0x80, 0x3F); // 1.0f little-endian + } + + [Theory] + [InlineData(0f)] + [InlineData(1.0f)] + [InlineData(-1.5f)] + [InlineData(float.MaxValue)] + [InlineData(float.MinValue)] + [InlineData(float.NaN)] + [InlineData(float.PositiveInfinity)] + [InlineData(float.NegativeInfinity)] + public void WriteAndReadSingleLittleEndian_should_roundtrip_correctly(float value) + { + Span buffer = new byte[4]; + + BinaryPrimitivesCompat.WriteSingleLittleEndian(buffer, value); + float result = BinaryPrimitivesCompat.ReadSingleLittleEndian(buffer); + + if (float.IsNaN(value)) + { + Assert.True(float.IsNaN(result)); + } + else + { + Assert.Equal(value, result); + } + } + } +} diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/BinaryVectorSerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/BinaryVectorSerializerTests.cs index 4394c626c76..274e93de373 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/BinaryVectorSerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/BinaryVectorSerializerTests.cs @@ -365,10 +365,16 @@ private BsonBinaryData SerializeToBinaryData(TCollection collection private static (T[], byte[] VectorBson) GetTestData(BinaryVectorDataType dataType, int elementsCount, byte bitsPadding) where T : struct { - var elementsSpan = new ReadOnlySpan(Enumerable.Range(0, elementsCount).Select(i => Convert.ChangeType(i, typeof(T)).As()).ToArray()); - byte[] vectorBsonData = [(byte)dataType, bitsPadding, .. MemoryMarshal.Cast(elementsSpan)]; - - return (elementsSpan.ToArray(), vectorBsonData); + var elementsSpan = new ReadOnlySpan( + Enumerable.Range(0, elementsCount) + .Select(i => Convert.ChangeType(i, typeof(T)).As()) + .ToArray()); + var elementsBytesLittleEndian = BitConverter.IsLittleEndian + ? MemoryMarshal.Cast(elementsSpan) + : BigEndianToLittleEndian(elementsSpan, dataType); + + byte[] vectorBsonData = [(byte)dataType, bitsPadding, .. elementsBytesLittleEndian]; + return (elementsSpan.ToArray(), vectorBsonData); } private static (BinaryVector, byte[] VectorBson) GetTestDataBinaryVector(BinaryVectorDataType dataType, int elementsCount, byte bitsPadding) @@ -409,6 +415,27 @@ private static IBsonSerializer CreateBinaryVectorSerializer(BinaryVectorDataT return serializer; } + private static byte[] BigEndianToLittleEndian(ReadOnlySpan span, BinaryVectorDataType dataType) where T : struct + { + // Types that do NOT need conversion safe on BE + if (dataType == BinaryVectorDataType.Int8 || dataType == BinaryVectorDataType.PackedBit) + { + return MemoryMarshal.Cast(span).ToArray(); + } + + var elementSize = Marshal.SizeOf(); + byte[] result = new byte[span.Length * elementSize]; + + for (int i = 0; i < span.Length; i++) + { + byte[] bytes = BitConverter.GetBytes((dynamic)span[i]); + Array.Reverse(bytes); // Ensure LE order + Buffer.BlockCopy(bytes, 0, result, i * elementSize, elementSize); + } + + return result; + } + public class BinaryVectorNoAttributeHolder { public BinaryVectorInt8 ValuesInt8 { get; set; } From cacbcf8e1b6d8bdb0465769753b5aa85126fde3c Mon Sep 17 00:00:00 2001 From: rstam Date: Thu, 15 May 2025 10:11:25 -0700 Subject: [PATCH 17/24] CSHARP-5587: FindOneAndUpdate should insert correct discriminator value on upsert --- .../FilteredMongoCollectionBase.cs | 8 +- .../Jira/CSharp5587Tests.cs | 114 ++++++++++++++++++ 2 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5587Tests.cs diff --git a/src/MongoDB.Driver/FilteredMongoCollectionBase.cs b/src/MongoDB.Driver/FilteredMongoCollectionBase.cs index 65d95d2f7b5..17a7a3e1901 100644 --- a/src/MongoDB.Driver/FilteredMongoCollectionBase.cs +++ b/src/MongoDB.Driver/FilteredMongoCollectionBase.cs @@ -304,22 +304,22 @@ public override Task> DistinctManyAsync(IClientSessio public override TProjection FindOneAndUpdate(FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { - return _wrappedCollection.FindOneAndUpdate(CombineFilters(filter), update, options, cancellationToken); + return _wrappedCollection.FindOneAndUpdate(CombineFilters(filter), AdjustUpdateDefinition(update, options.IsUpsert), options, cancellationToken); } public override TProjection FindOneAndUpdate(IClientSessionHandle session, FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { - return _wrappedCollection.FindOneAndUpdate(session, CombineFilters(filter), update, options, cancellationToken); + return _wrappedCollection.FindOneAndUpdate(session, CombineFilters(filter), AdjustUpdateDefinition(update, options.IsUpsert), options, cancellationToken); } public override Task FindOneAndUpdateAsync(FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { - return _wrappedCollection.FindOneAndUpdateAsync(CombineFilters(filter), update, options, cancellationToken); + return _wrappedCollection.FindOneAndUpdateAsync(CombineFilters(filter), AdjustUpdateDefinition(update, options.IsUpsert), options, cancellationToken); } public override Task FindOneAndUpdateAsync(IClientSessionHandle session, FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { - return _wrappedCollection.FindOneAndUpdateAsync(session, CombineFilters(filter), update, options, cancellationToken); + return _wrappedCollection.FindOneAndUpdateAsync(session, CombineFilters(filter), AdjustUpdateDefinition(update, options.IsUpsert), options, cancellationToken); } [Obsolete("Use Aggregation pipeline instead.")] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5587Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5587Tests.cs new file mode 100644 index 00000000000..3c9d46ce032 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5587Tests.cs @@ -0,0 +1,114 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System.Collections.Generic; +using System.Linq; +using MongoDB.Driver.TestHelpers; +using FluentAssertions; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira; + +public class CSharp5587Tests : LinqIntegrationTest +{ + public CSharp5587Tests(ClassFixture fixture) + : base(fixture) + { + } + + [Fact] + public void FindOneAndUpdate_should_use_correct_discriminator() + { + var collection = Fixture.Collection; + + var lion1 = new Lion { Id = 1, Name = "Lion1" }; + var updateDefinition1 = Builders.Update + .SetOnInsert(l => l.Id, 1) + .Set(l => l.Name, lion1.Name); + collection.OfType().FindOneAndUpdate( + f => f.Name == lion1.Name, + updateDefinition1, + new FindOneAndUpdateOptions { IsUpsert = true }); + + var result = collection.AsQueryable().As(BsonDocumentSerializer.Instance).Single(); + result.Should().BeEquivalentTo( + """ + { + _id : 1, + _t : ["Animal", "Cat", "Lion"], + Name : "Lion1" + } + """); + } + + [Fact] + public void UpdateOne_should_use_correct_discriminator() + { + var collection = Fixture.Collection; + + var lion2 = new Lion { Id = 2, Name = "Lion2" }; + var updateDefinition2 = Builders.Update + .SetOnInsert(l => l.Id, lion2.Id) + .Set(l => l.Name, lion2.Name); + collection.OfType().UpdateOne( + f => f.Name == lion2.Name, + updateDefinition2, + new UpdateOptions { IsUpsert = true }); + + var result = collection.AsQueryable().As(BsonDocumentSerializer.Instance).Single(); + result.Should().BeEquivalentTo( + """ + { + _id : 2, + _t : ["Animal", "Cat", "Lion"], + Name : "Lion2" + } + """); + } + + [BsonDiscriminator(RootClass = true)] + [BsonKnownTypes(typeof(Cat), typeof(Dog))] + public class Animal + { + public int Id { get; set; } + } + + [BsonKnownTypes(typeof(Lion), typeof(Tiger))] + public class Cat : Animal + { + } + + public class Dog : Animal + { + } + + public class Lion : Cat + { + public string Name { get; set; } + } + public class Tiger : Cat + { + } + + public sealed class ClassFixture : MongoCollectionFixture + { + public override bool InitializeDataBeforeEachTestCase => true; + + protected override IEnumerable InitialData => null; + } +} From 064fc63b3a17c1ee08ea8344b5658fabbe8b4181 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 12 Jun 2025 10:46:04 +0200 Subject: [PATCH 18/24] CSHARP-3494: Fix discriminator for generic types (#1685) --- .../Serialization/BsonClassMap.cs | 2 +- .../MongoDB.Bson.Tests/Jira/CSharp515Tests.cs | 2 +- .../Serializers/DiscriminatorTests.cs | 75 +++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/BsonClassMap.cs b/src/MongoDB.Bson/Serialization/BsonClassMap.cs index 2781d218d6b..0f524fe2326 100644 --- a/src/MongoDB.Bson/Serialization/BsonClassMap.cs +++ b/src/MongoDB.Bson/Serialization/BsonClassMap.cs @@ -1057,7 +1057,7 @@ public void Reset() _creatorMaps.Clear(); _creator = null; _declaredMemberMaps = new List(); - _discriminator = _classType.Name; + _discriminator = BsonUtils.GetFriendlyTypeName(_classType); _discriminatorIsRequired = false; _extraElementsMemberMap = null; _idMemberMap = null; diff --git a/tests/MongoDB.Bson.Tests/Jira/CSharp515Tests.cs b/tests/MongoDB.Bson.Tests/Jira/CSharp515Tests.cs index 27de1f7abaa..04350931f01 100644 --- a/tests/MongoDB.Bson.Tests/Jira/CSharp515Tests.cs +++ b/tests/MongoDB.Bson.Tests/Jira/CSharp515Tests.cs @@ -45,7 +45,7 @@ public S(IList list) } private static readonly string __discriminatorAssemblyName = "MongoDB.Bson.Tests"; - private string _jsonTemplate = ("{ '_id' : 1, 'R' : #V, 'S' : #V, 'RS' : { '_t' : 'S`1', '_v' : #V }, 'OR' : { '_t' : 'System.Collections.ObjectModel.ReadOnlyCollection`1[System.Int32]', '_v' : #V }, 'OS' : { '_t' : 'MongoDB.Bson.Tests.Jira.CSharp515.CSharp515Tests+S`1[System.Int32], " + __discriminatorAssemblyName + "', '_v' : #V } }").Replace("'", "\""); + private string _jsonTemplate = ("{ '_id' : 1, 'R' : #V, 'S' : #V, 'RS' : { '_t' : 'S', '_v' : #V }, 'OR' : { '_t' : 'System.Collections.ObjectModel.ReadOnlyCollection`1[System.Int32]', '_v' : #V }, 'OS' : { '_t' : 'MongoDB.Bson.Tests.Jira.CSharp515.CSharp515Tests+S`1[System.Int32], " + __discriminatorAssemblyName + "', '_v' : #V } }").Replace("'", "\""); [Fact] public void TestNull() diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DiscriminatorTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DiscriminatorTests.cs index 78fe458a3aa..def1a5e027b 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DiscriminatorTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DiscriminatorTests.cs @@ -13,7 +13,9 @@ * limitations under the License. */ +using System.Collections.Generic; using System.Linq; +using FluentAssertions; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.TestHelpers; @@ -65,6 +67,79 @@ private class H : G { } + // BaseDocument and derived classes are used for tests with generic types + // It's necessary to specify the derived specific types with BsonKnownTypes for the deserialization to work. + [BsonKnownTypes(typeof(DerivedDocument))] + [BsonKnownTypes(typeof(DerivedDocument>>))] + [BsonKnownTypes(typeof(DerivedDocumentDouble))] + abstract class BaseDocument; + + class DerivedDocument : BaseDocument + { + [BsonId] + public int Id { get; set; } + + public T Value { get; set; } + } + + class DerivedDocumentDouble : BaseDocument + { + [BsonId] + public int Id { get; set; } + + public T1 Value1 { get; set; } + + public T2 Value2 { get; set; } + } + + [Fact] + public void TestDeserializeGenericType() + { + var serialized = """{ "_t" : "DerivedDocument", "_id" : 1, "Value" : 42 }"""; + var rehydrated = BsonSerializer.Deserialize(serialized); + rehydrated.Should().BeOfType>(); + } + + [Fact] + public void TestDeserializeGenericTypeWithNestedType() + { + var serialized = """{ "_t" : "DerivedDocument>>", "_id" : 1, "Value" : [{ "key" : 1 }] }"""; + var rehydrated = BsonSerializer.Deserialize(serialized); + rehydrated.Should().BeOfType>>>(); + } + + [Fact] + public void TestDeserializeGenericTypeWithTwoTypes() + { + var serialized = """{ "_t" : "DerivedDocumentDouble", "_id" : 1, "Value1" : 42, "Value2" : "hello" }"""; + var rehydrated = BsonSerializer.Deserialize(serialized); + rehydrated.Should().BeOfType>(); + } + + [Fact] + public void TestSerializeGenericType() + { + var document = new DerivedDocument { Id = 1, Value = 42 }; + var serialized = document.ToJson(typeof(BaseDocument)); + serialized.Should().Be("""{ "_t" : "DerivedDocument", "_id" : 1, "Value" : 42 }"""); + } + + [Fact] + public void TestSerializeGenericTypeWithNestedType() + { + var document = new DerivedDocument>> { Id = 1, Value = [new() { { "key", 1 } }] }; + var serialized = document.ToJson(typeof(BaseDocument)); + serialized.Should().Be("""{ "_t" : "DerivedDocument>>", "_id" : 1, "Value" : [{ "key" : 1 }] }"""); + } + + [Fact] + public void TestSerializeGenericTypeWithTwoTypes() + { + var document = new DerivedDocumentDouble { Id = 1, Value1 = 42, Value2 = "hello"}; + var serialized = document.ToJson(typeof(BaseDocument)); + serialized.Should().Be("""{ "_t" : "DerivedDocumentDouble", "_id" : 1, "Value1" : 42, "Value2" : "hello" }"""); + } + [Fact] public void TestSerializeObjectasObject() { From cc693396f0d346aad61561e9e5c9ff4dd599a79c Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Thu, 12 Jun 2025 08:39:09 -0700 Subject: [PATCH 19/24] CSHARP-3550: CSOT: Server Selection (#1705) --- .../Core/Bindings/ChannelChannelSource.cs | 5 +- .../Core/Bindings/ChannelReadBinding.cs | 13 +- .../Core/Bindings/ChannelReadWriteBinding.cs | 41 ++- .../Core/Bindings/ChannelSourceHandle.cs | 9 +- .../Bindings/ChannelSourceReadWriteBinding.cs | 41 ++- .../Core/Bindings/CoreSession.cs | 34 ++- src/MongoDB.Driver/Core/Bindings/IBinding.cs | 24 +- .../Core/Bindings/IChannelSource.cs | 5 +- .../Core/Bindings/ReadBindingHandle.cs | 17 +- .../Core/Bindings/ReadPreferenceBinding.cs | 17 +- .../Core/Bindings/ReadWriteBindingHandle.cs | 49 ++- .../Core/Bindings/ServerChannelSource.cs | 9 +- .../Core/Bindings/SingleServerReadBinding.cs | 12 +- .../Bindings/SingleServerReadWriteBinding.cs | 41 ++- .../Core/Bindings/WritableServerBinding.cs | 49 ++- src/MongoDB.Driver/Core/Clusters/Cluster.cs | 159 +++------- src/MongoDB.Driver/Core/Clusters/ICluster.cs | 5 +- .../Core/Clusters/IClusterExtensions.cs | 17 +- .../Core/Clusters/LoadBalancedCluster.cs | 38 ++- .../ExclusiveConnectionPool.Helpers.cs | 146 ++++----- .../ExclusiveConnectionPool.cs | 11 +- .../Core/ConnectionPools/IConnectionPool.cs | 5 +- .../Core/ConnectionPools/MaintenanceHelper.cs | 4 +- src/MongoDB.Driver/Core/Misc/Feature.cs | 12 +- .../Core/Operations/AggregateOperation.cs | 21 +- .../AggregateToCollectionOperation.cs | 17 +- .../Core/Operations/AsyncCursor.cs | 18 +- .../Operations/BulkMixedWriteOperation.cs | 21 +- .../BulkUnmixedWriteOperationBase.cs | 37 ++- .../Core/Operations/ChangeStreamCursor.cs | 8 +- .../Core/Operations/ChangeStreamOperation.cs | 37 ++- .../Operations/ClientBulkWriteOperation.cs | 19 +- .../Core/Operations/CommandOperationBase.cs | 16 +- .../Operations/CompositeWriteOperation.cs | 9 +- .../Operations/CountDocumentsOperation.cs | 13 +- .../Core/Operations/CountOperation.cs | 21 +- .../Operations/CreateCollectionOperation.cs | 17 +- .../Core/Operations/CreateIndexesOperation.cs | 17 +- .../CreateSearchIndexesOperation.cs | 17 +- .../Core/Operations/CreateViewOperation.cs | 17 +- .../Operations/DatabaseExistsOperation.cs | 15 +- .../Core/Operations/DistinctOperation.cs | 13 +- .../Operations/DropCollectionOperation.cs | 17 +- .../Core/Operations/DropDatabaseOperation.cs | 21 +- .../Core/Operations/DropIndexOperation.cs | 19 +- .../Operations/DropSearchIndexOperation.cs | 17 +- .../Operations/EndTransactionOperation.cs | 25 +- .../EstimatedDocumentCountOperation.cs | 13 +- .../Core/Operations/EvalOperation.cs | 13 +- .../Operations/FindAndModifyOperationBase.cs | 35 ++- .../Core/Operations/FindOperation.cs | 21 +- .../Core/Operations/GroupOperation.cs | 17 +- .../Core/Operations/IOperation.cs | 9 +- .../Core/Operations/IRetryableOperation.cs | 17 +- .../Operations/ListCollectionsOperation.cs | 21 +- .../Core/Operations/ListDatabasesOperation.cs | 13 +- .../Core/Operations/ListIndexesOperation.cs | 13 +- .../ListIndexesUsingCommandOperation.cs | 21 +- .../Core/Operations/MapReduceOperation.cs | 17 +- .../MapReduceOutputToCollectionOperation.cs | 17 +- .../Operations/OperationExtensionMethods.cs | 25 +- .../Core/Operations/PingOperation.cs | 71 ----- .../Core/Operations/ReadCommandOperation.cs | 29 +- .../Operations/RenameCollectionOperation.cs | 21 +- .../Core/Operations/RetryableReadContext.cs | 29 +- .../RetryableReadOperationExecutor.cs | 37 ++- .../RetryableWriteCommandOperationBase.cs | 33 +- .../Core/Operations/RetryableWriteContext.cs | 29 +- .../RetryableWriteOperationExecutor.cs | 37 ++- .../Operations/UpdateSearchIndexOperation.cs | 17 +- .../Core/Operations/WriteCommandOperation.cs | 13 +- src/MongoDB.Driver/Core/Servers/IServer.cs | 4 +- src/MongoDB.Driver/Core/Servers/Server.cs | 8 +- src/MongoDB.Driver/GridFS/GridFSBucket.cs | 272 ++++++++++------- .../GridFS/GridFSForwardOnlyDownloadStream.cs | 8 +- .../GridFS/GridFSForwardOnlyUploadStream.cs | 8 +- .../GridFS/GridFSSeekableDownloadStream.cs | 10 +- src/MongoDB.Driver/MongoClient.cs | 5 +- src/MongoDB.Driver/MongoCollectionImpl.cs | 5 +- src/MongoDB.Driver/MongoDatabase.cs | 6 +- src/MongoDB.Driver/OperationContext.cs | 161 ++++++++++ src/MongoDB.Driver/OperationExecutor.cs | 12 +- src/MongoDB.Driver/OperationOptionsBase.cs | 27 ++ src/MongoDB.Driver/ReadOperationOptions.cs | 5 +- src/MongoDB.Driver/WriteOperationOptions.cs | 5 +- .../Core/CoreTestConfiguration.cs | 12 +- .../Core/FailPoint.cs | 4 +- .../Core/MockClusterableServerFactory.cs | 6 +- .../DriverTestConfiguration.cs | 4 +- .../AuthenticationTests.cs | 5 +- tests/MongoDB.Driver.Tests/ClusterTests.cs | 8 +- .../Bindings/ChannelChannelSourceTests.cs | 38 +-- .../Core/Bindings/ChannelReadBindingTests.cs | 39 +-- .../Bindings/ChannelReadWriteBindingTests.cs | 74 +---- .../Core/Bindings/ChannelSourceHandleTests.cs | 27 +- .../ChannelSourceReadWriteBindingTests.cs | 68 ++--- .../Core/Bindings/ReadBindingHandleTests.cs | 28 +- .../Bindings/ReadPreferenceBindingTests.cs | 52 ++-- .../Bindings/ReadWriteBindingHandleTests.cs | 55 ++-- .../Core/Bindings/ServerChannelSourceTests.cs | 41 +-- .../Bindings/SingleServerReadBindingTests.cs | 38 +-- .../SingleServerReadWriteBindingTests.cs | 76 +---- .../Bindings/WritableServerBindingTests.cs | 87 +++--- .../Core/Clusters/ClusterTests.cs | 216 ++++---------- .../Core/Clusters/LoadBalancedClusterTests.cs | 51 ++-- .../ExclusiveConnectionPoolTests.cs | 192 ++++-------- .../ConnectionPools/MaintenanceHelperTests.cs | 4 +- .../Core/Jira/CSharp3173Tests.cs | 12 +- .../Core/Jira/CSharp3302Tests.cs | 10 +- .../Core/LoadBalancingIntergationTests.cs | 17 +- .../Operations/AggregateOperationTests.cs | 11 +- .../Core/Operations/AsyncCursorTests.cs | 84 +----- .../BulkMixedWriteOperationTests.cs | 16 +- .../Operations/ChangeStreamCursorTests.cs | 34 +-- .../Operations/ChangeStreamOperationTests.cs | 10 +- .../CompositeWriteOperationTests.cs | 21 +- .../CreateCollectionOperationTests.cs | 6 +- .../Operations/CreateViewOperationTests.cs | 2 +- .../Core/Operations/EvalOperationTests.cs | 4 +- .../Core/Operations/GroupOperationTests.cs | 12 +- .../Core/Operations/OperationTestBase.cs | 60 ++-- .../Operations/ReadCommandOperationTests.cs | 68 ++--- .../RetryableWriteOperationExecutorTests.cs | 6 +- .../Operations/WriteCommandOperationTests.cs | 47 +-- .../Core/Servers/LoadBalancedServerTests.cs | 132 +++----- .../Core/Servers/ServerTests.cs | 170 ++++------- .../Encryption/ClientEncryptionTests.cs | 16 +- .../JsonDrivenConfigureFailPointTest.cs | 4 +- .../JsonDrivenTargetedFailPointTest.cs | 7 +- .../OperationContextTests.cs | 281 ++++++++++++++++++ .../OperationExecutorTests.cs | 34 +-- .../ReadOperationOptionsTests.cs | 3 +- .../MongoClientJsonDrivenTestRunnerBase.cs | 4 +- .../prose-tests/ClientEncryptionProseTests.cs | 2 +- ...onnectionMonitoringAndPoolingTestRunner.cs | 19 +- .../RetryableReadsProseTests.cs | 8 +- .../prose-tests/PoolClearRetryability.cs | 3 +- .../prose-tests/RetryWriteOnOtherMongos.cs | 6 +- .../ServerDiscoveryAndMonitoringProseTests.cs | 6 +- .../server-selection/InWindowTestRunner.cs | 5 +- .../UnifiedTargetedFailPointOperation.cs | 3 +- 141 files changed, 2132 insertions(+), 2415 deletions(-) delete mode 100644 src/MongoDB.Driver/Core/Operations/PingOperation.cs create mode 100644 src/MongoDB.Driver/OperationContext.cs create mode 100644 src/MongoDB.Driver/OperationOptionsBase.cs create mode 100644 tests/MongoDB.Driver.Tests/OperationContextTests.cs diff --git a/src/MongoDB.Driver/Core/Bindings/ChannelChannelSource.cs b/src/MongoDB.Driver/Core/Bindings/ChannelChannelSource.cs index 26f7ee2881d..f48a8038428 100644 --- a/src/MongoDB.Driver/Core/Bindings/ChannelChannelSource.cs +++ b/src/MongoDB.Driver/Core/Bindings/ChannelChannelSource.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -64,13 +63,13 @@ public void Dispose() } } - public IChannelHandle GetChannel(CancellationToken cancellationToken) + public IChannelHandle GetChannel(OperationContext operationContext) { ThrowIfDisposed(); return GetChannelHelper(); } - public Task GetChannelAsync(CancellationToken cancellationToken) + public Task GetChannelAsync(OperationContext operationContext) { ThrowIfDisposed(); return Task.FromResult(GetChannelHelper()); diff --git a/src/MongoDB.Driver/Core/Bindings/ChannelReadBinding.cs b/src/MongoDB.Driver/Core/Bindings/ChannelReadBinding.cs index 81173ad3639..63dab353cf4 100644 --- a/src/MongoDB.Driver/Core/Bindings/ChannelReadBinding.cs +++ b/src/MongoDB.Driver/Core/Bindings/ChannelReadBinding.cs @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -58,26 +57,26 @@ public void Dispose() } } - public IChannelSourceHandle GetReadChannelSource(CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext) { ThrowIfDisposed(); return GetReadChannelSourceHelper(); } - public Task GetReadChannelSourceAsync(CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext) { ThrowIfDisposed(); return Task.FromResult(GetReadChannelSourceHelper()); } - public IChannelSourceHandle GetReadChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetReadChannelSource(cancellationToken); + return GetReadChannelSource(operationContext); } - public Task GetReadChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetReadChannelSourceAsync(cancellationToken); + return GetReadChannelSourceAsync(operationContext); } private IChannelSourceHandle GetReadChannelSourceHelper() diff --git a/src/MongoDB.Driver/Core/Bindings/ChannelReadWriteBinding.cs b/src/MongoDB.Driver/Core/Bindings/ChannelReadWriteBinding.cs index 71a8e76c429..17ae75966bc 100644 --- a/src/MongoDB.Driver/Core/Bindings/ChannelReadWriteBinding.cs +++ b/src/MongoDB.Driver/Core/Bindings/ChannelReadWriteBinding.cs @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -56,68 +55,68 @@ public void Dispose() } } - public IChannelSourceHandle GetReadChannelSource(CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext) { ThrowIfDisposed(); return GetChannelSourceHelper(); } - public Task GetReadChannelSourceAsync(CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext) { ThrowIfDisposed(); return Task.FromResult(GetChannelSourceHelper()); } - public IChannelSourceHandle GetReadChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetReadChannelSource(cancellationToken); + return GetReadChannelSource(operationContext); } - public Task GetReadChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetReadChannelSourceAsync(cancellationToken); + return GetReadChannelSourceAsync(operationContext); } - public IChannelSourceHandle GetWriteChannelSource(CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext) { ThrowIfDisposed(); return GetChannelSourceHelper(); } - public IChannelSourceHandle GetWriteChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetWriteChannelSource(cancellationToken); + return GetWriteChannelSource(operationContext); } - public IChannelSourceHandle GetWriteChannelSource(IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IMayUseSecondaryCriteria mayUseSecondary) { - return GetWriteChannelSource(cancellationToken); // ignore mayUseSecondary + return GetWriteChannelSource(operationContext); // ignore mayUseSecondary } - public IChannelSourceHandle GetWriteChannelSource(IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary) { - return GetWriteChannelSource(mayUseSecondary, cancellationToken); + return GetWriteChannelSource(operationContext, mayUseSecondary); } - public Task GetWriteChannelSourceAsync(CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext) { ThrowIfDisposed(); return Task.FromResult(GetChannelSourceHelper()); } - public Task GetWriteChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetWriteChannelSourceAsync(cancellationToken); + return GetWriteChannelSourceAsync(operationContext); } - public Task GetWriteChannelSourceAsync(IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext, IMayUseSecondaryCriteria mayUseSecondary) { - return GetWriteChannelSourceAsync(cancellationToken); // ignore mayUseSecondary + return GetWriteChannelSourceAsync(operationContext); // ignore mayUseSecondary } - public Task GetWriteChannelSourceAsync(IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary) { - return GetWriteChannelSourceAsync(mayUseSecondary, cancellationToken); + return GetWriteChannelSourceAsync(operationContext, mayUseSecondary); } private IChannelSourceHandle GetChannelSourceHelper() diff --git a/src/MongoDB.Driver/Core/Bindings/ChannelSourceHandle.cs b/src/MongoDB.Driver/Core/Bindings/ChannelSourceHandle.cs index c7c04912d7b..3b08ff9da33 100644 --- a/src/MongoDB.Driver/Core/Bindings/ChannelSourceHandle.cs +++ b/src/MongoDB.Driver/Core/Bindings/ChannelSourceHandle.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -55,16 +54,16 @@ public ICoreSessionHandle Session } // methods - public IChannelHandle GetChannel(CancellationToken cancellationToken) + public IChannelHandle GetChannel(OperationContext operationContext) { ThrowIfDisposed(); - return _reference.Instance.GetChannel(cancellationToken); + return _reference.Instance.GetChannel(operationContext); } - public Task GetChannelAsync(CancellationToken cancellationToken) + public Task GetChannelAsync(OperationContext operationContext) { ThrowIfDisposed(); - return _reference.Instance.GetChannelAsync(cancellationToken); + return _reference.Instance.GetChannelAsync(operationContext); } public void Dispose() diff --git a/src/MongoDB.Driver/Core/Bindings/ChannelSourceReadWriteBinding.cs b/src/MongoDB.Driver/Core/Bindings/ChannelSourceReadWriteBinding.cs index c6fef30f1e6..e0c4772d23f 100644 --- a/src/MongoDB.Driver/Core/Bindings/ChannelSourceReadWriteBinding.cs +++ b/src/MongoDB.Driver/Core/Bindings/ChannelSourceReadWriteBinding.cs @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -46,68 +45,68 @@ public ICoreSessionHandle Session get { return _session; } } - public IChannelSourceHandle GetReadChannelSource(CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext) { ThrowIfDisposed(); return GetChannelSourceHelper(); } - public Task GetReadChannelSourceAsync(CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext) { ThrowIfDisposed(); return Task.FromResult(GetChannelSourceHelper()); } - public IChannelSourceHandle GetReadChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetReadChannelSource(cancellationToken); + return GetReadChannelSource(operationContext); } - public Task GetReadChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetReadChannelSourceAsync(cancellationToken); + return GetReadChannelSourceAsync(operationContext); } - public IChannelSourceHandle GetWriteChannelSource(CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext) { ThrowIfDisposed(); return GetChannelSourceHelper(); } - public IChannelSourceHandle GetWriteChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetWriteChannelSource(cancellationToken); + return GetWriteChannelSource(operationContext); } - public IChannelSourceHandle GetWriteChannelSource(IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IMayUseSecondaryCriteria mayUseSecondary) { - return GetWriteChannelSource(cancellationToken); // ignore mayUseSecondary + return GetWriteChannelSource(operationContext); // ignore mayUseSecondary } - public IChannelSourceHandle GetWriteChannelSource(IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary) { - return GetWriteChannelSource(mayUseSecondary, cancellationToken); + return GetWriteChannelSource(operationContext, mayUseSecondary); } - public Task GetWriteChannelSourceAsync(CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext) { ThrowIfDisposed(); return Task.FromResult(GetChannelSourceHelper()); } - public Task GetWriteChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetWriteChannelSourceAsync(cancellationToken); + return GetWriteChannelSourceAsync(operationContext); } - public Task GetWriteChannelSourceAsync(IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext, IMayUseSecondaryCriteria mayUseSecondary) { - return GetWriteChannelSourceAsync(cancellationToken); // ignore mayUseSecondary + return GetWriteChannelSourceAsync(operationContext); // ignore mayUseSecondary } - public Task GetWriteChannelSourceAsync(IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary) { - return GetWriteChannelSourceAsync(mayUseSecondary, cancellationToken); + return GetWriteChannelSourceAsync(operationContext, mayUseSecondary); } public void Dispose() diff --git a/src/MongoDB.Driver/Core/Bindings/CoreSession.cs b/src/MongoDB.Driver/Core/Bindings/CoreSession.cs index 66fa294a14f..073492ea4da 100644 --- a/src/MongoDB.Driver/Core/Bindings/CoreSession.cs +++ b/src/MongoDB.Driver/Core/Bindings/CoreSession.cs @@ -145,6 +145,8 @@ public bool IsInTransaction { EnsureAbortTransactionCanBeCalled(nameof(AbortTransaction)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); try { if (_currentTransaction.IsEmpty) @@ -155,7 +157,7 @@ public bool IsInTransaction try { var firstAttempt = CreateAbortTransactionOperation(); - ExecuteEndTransactionOnPrimary(firstAttempt, cancellationToken); + ExecuteEndTransactionOnPrimary(operationContext, firstAttempt); return; } catch (Exception exception) when (ShouldRetryEndTransactionException(exception)) @@ -173,7 +175,7 @@ public bool IsInTransaction try { var secondAttempt = CreateAbortTransactionOperation(); - ExecuteEndTransactionOnPrimary(secondAttempt, cancellationToken); + ExecuteEndTransactionOnPrimary(operationContext, secondAttempt); } catch { @@ -194,6 +196,8 @@ public bool IsInTransaction { EnsureAbortTransactionCanBeCalled(nameof(AbortTransaction)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); try { if (_currentTransaction.IsEmpty) @@ -204,7 +208,7 @@ public bool IsInTransaction try { var firstAttempt = CreateAbortTransactionOperation(); - await ExecuteEndTransactionOnPrimaryAsync(firstAttempt, cancellationToken).ConfigureAwait(false); + await ExecuteEndTransactionOnPrimaryAsync(operationContext, firstAttempt).ConfigureAwait(false); return; } catch (Exception exception) when (ShouldRetryEndTransactionException(exception)) @@ -222,7 +226,7 @@ public bool IsInTransaction try { var secondAttempt = CreateAbortTransactionOperation(); - await ExecuteEndTransactionOnPrimaryAsync(secondAttempt, cancellationToken).ConfigureAwait(false); + await ExecuteEndTransactionOnPrimaryAsync(operationContext, secondAttempt).ConfigureAwait(false); } catch { @@ -292,6 +296,8 @@ public long AdvanceTransactionNumber() { EnsureCommitTransactionCanBeCalled(nameof(CommitTransaction)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); try { _isCommitTransactionInProgress = true; @@ -303,7 +309,7 @@ public long AdvanceTransactionNumber() try { var firstAttempt = CreateCommitTransactionOperation(IsFirstCommitAttemptRetry()); - ExecuteEndTransactionOnPrimary(firstAttempt, cancellationToken); + ExecuteEndTransactionOnPrimary(operationContext, firstAttempt); return; } catch (Exception exception) when (ShouldRetryEndTransactionException(exception)) @@ -313,7 +319,7 @@ public long AdvanceTransactionNumber() } var secondAttempt = CreateCommitTransactionOperation(isCommitRetry: true); - ExecuteEndTransactionOnPrimary(secondAttempt, cancellationToken); + ExecuteEndTransactionOnPrimary(operationContext, secondAttempt); } finally { @@ -327,6 +333,8 @@ public long AdvanceTransactionNumber() { EnsureCommitTransactionCanBeCalled(nameof(CommitTransaction)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); try { _isCommitTransactionInProgress = true; @@ -338,7 +346,7 @@ public long AdvanceTransactionNumber() try { var firstAttempt = CreateCommitTransactionOperation(IsFirstCommitAttemptRetry()); - await ExecuteEndTransactionOnPrimaryAsync(firstAttempt, cancellationToken).ConfigureAwait(false); + await ExecuteEndTransactionOnPrimaryAsync(operationContext, firstAttempt).ConfigureAwait(false); return; } catch (Exception exception) when (ShouldRetryEndTransactionException(exception)) @@ -348,7 +356,7 @@ public long AdvanceTransactionNumber() } var secondAttempt = CreateCommitTransactionOperation(isCommitRetry: true); - await ExecuteEndTransactionOnPrimaryAsync(secondAttempt, cancellationToken).ConfigureAwait(false); + await ExecuteEndTransactionOnPrimaryAsync(operationContext, secondAttempt).ConfigureAwait(false); } finally { @@ -404,7 +412,7 @@ public void StartTransaction(TransactionOptions transactionOptions = null) throw new InvalidOperationException("Transactions do not support unacknowledged write concerns."); } - _currentTransaction?.UnpinAll(); // unpin data if any when a new transaction is started + _currentTransaction?.UnpinAll(); // unpin data if any when a new transaction is started _currentTransaction = new CoreTransaction(transactionNumber, effectiveTransactionOptions); } @@ -537,21 +545,21 @@ private void EnsureTransactionsAreSupported() } } - private TResult ExecuteEndTransactionOnPrimary(IReadOperation operation, CancellationToken cancellationToken) + private TResult ExecuteEndTransactionOnPrimary(OperationContext operationContext, IReadOperation operation) { using (var sessionHandle = new NonDisposingCoreSessionHandle(this)) using (var binding = ChannelPinningHelper.CreateReadWriteBinding(_cluster, sessionHandle)) { - return operation.Execute(binding, cancellationToken); + return operation.Execute(operationContext, binding); } } - private async Task ExecuteEndTransactionOnPrimaryAsync(IReadOperation operation, CancellationToken cancellationToken) + private async Task ExecuteEndTransactionOnPrimaryAsync(OperationContext operationContext, IReadOperation operation) { using (var sessionHandle = new NonDisposingCoreSessionHandle(this)) using (var binding = ChannelPinningHelper.CreateReadWriteBinding(_cluster, sessionHandle)) { - return await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); } } diff --git a/src/MongoDB.Driver/Core/Bindings/IBinding.cs b/src/MongoDB.Driver/Core/Bindings/IBinding.cs index 304796f1a9b..275131043ba 100644 --- a/src/MongoDB.Driver/Core/Bindings/IBinding.cs +++ b/src/MongoDB.Driver/Core/Bindings/IBinding.cs @@ -30,24 +30,24 @@ internal interface IReadBinding : IBinding { ReadPreference ReadPreference { get; } - IChannelSourceHandle GetReadChannelSource(CancellationToken cancellationToken); - Task GetReadChannelSourceAsync(CancellationToken cancellationToken); + IChannelSourceHandle GetReadChannelSource(OperationContext operationContext); + Task GetReadChannelSourceAsync(OperationContext operationContext); - IChannelSourceHandle GetReadChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken); - Task GetReadChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken); + IChannelSourceHandle GetReadChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers); + Task GetReadChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers); } internal interface IWriteBinding : IBinding { - IChannelSourceHandle GetWriteChannelSource(CancellationToken cancellationToken); - IChannelSourceHandle GetWriteChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken); - IChannelSourceHandle GetWriteChannelSource(IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken); - IChannelSourceHandle GetWriteChannelSource(IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken); + IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext); + IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers); + IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IMayUseSecondaryCriteria mayUseSecondary); + IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary); - Task GetWriteChannelSourceAsync(CancellationToken cancellationToken); - Task GetWriteChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken); - Task GetWriteChannelSourceAsync(IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken); - Task GetWriteChannelSourceAsync(IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken); + Task GetWriteChannelSourceAsync(OperationContext operationContext); + Task GetWriteChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers); + Task GetWriteChannelSourceAsync(OperationContext operationContext, IMayUseSecondaryCriteria mayUseSecondary); + Task GetWriteChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary); } internal interface IReadWriteBinding : IReadBinding, IWriteBinding diff --git a/src/MongoDB.Driver/Core/Bindings/IChannelSource.cs b/src/MongoDB.Driver/Core/Bindings/IChannelSource.cs index 8582a50e7b8..c9bd90ec61b 100644 --- a/src/MongoDB.Driver/Core/Bindings/IChannelSource.cs +++ b/src/MongoDB.Driver/Core/Bindings/IChannelSource.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Servers; @@ -26,8 +25,8 @@ internal interface IChannelSource : IDisposable ServerDescription ServerDescription { get; } ICoreSessionHandle Session { get; } - IChannelHandle GetChannel(CancellationToken cancellationToken); - Task GetChannelAsync(CancellationToken cancellationToken); + IChannelHandle GetChannel(OperationContext operationContext); + Task GetChannelAsync(OperationContext operationContext); } internal interface IChannelSourceHandle : IChannelSource diff --git a/src/MongoDB.Driver/Core/Bindings/ReadBindingHandle.cs b/src/MongoDB.Driver/Core/Bindings/ReadBindingHandle.cs index 63605e4b6c3..99dfa24212d 100644 --- a/src/MongoDB.Driver/Core/Bindings/ReadBindingHandle.cs +++ b/src/MongoDB.Driver/Core/Bindings/ReadBindingHandle.cs @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -47,28 +46,28 @@ public ICoreSessionHandle Session get { return _reference.Instance.Session; } } - public IChannelSourceHandle GetReadChannelSource(CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext) { ThrowIfDisposed(); - return _reference.Instance.GetReadChannelSource(cancellationToken); + return _reference.Instance.GetReadChannelSource(operationContext); } - public Task GetReadChannelSourceAsync(CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext) { ThrowIfDisposed(); - return _reference.Instance.GetReadChannelSourceAsync(cancellationToken); + return _reference.Instance.GetReadChannelSourceAsync(operationContext); } - public IChannelSourceHandle GetReadChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { ThrowIfDisposed(); - return _reference.Instance.GetReadChannelSource(deprioritizedServers, cancellationToken); + return _reference.Instance.GetReadChannelSource(operationContext, deprioritizedServers); } - public Task GetReadChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { ThrowIfDisposed(); - return _reference.Instance.GetReadChannelSourceAsync(deprioritizedServers, cancellationToken); + return _reference.Instance.GetReadChannelSourceAsync(operationContext, deprioritizedServers); } public void Dispose() diff --git a/src/MongoDB.Driver/Core/Bindings/ReadPreferenceBinding.cs b/src/MongoDB.Driver/Core/Bindings/ReadPreferenceBinding.cs index 54cd2faf4a2..32106f0efcd 100644 --- a/src/MongoDB.Driver/Core/Bindings/ReadPreferenceBinding.cs +++ b/src/MongoDB.Driver/Core/Bindings/ReadPreferenceBinding.cs @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Clusters.ServerSelectors; @@ -52,27 +51,27 @@ public ICoreSessionHandle Session get { return _session; } } - public IChannelSourceHandle GetReadChannelSource(CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext) { - return GetReadChannelSource(null, cancellationToken); + return GetReadChannelSource(operationContext, null); } - public Task GetReadChannelSourceAsync(CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext) { - return GetReadChannelSourceAsync(null, cancellationToken); + return GetReadChannelSourceAsync(operationContext, null); } - public IChannelSourceHandle GetReadChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { ThrowIfDisposed(); - var server = _cluster.SelectServerAndPinIfNeeded(_session, _serverSelector, deprioritizedServers, cancellationToken); + var server = _cluster.SelectServerAndPinIfNeeded(operationContext, _session, _serverSelector, deprioritizedServers); return GetChannelSourceHelper(server); } - public async Task GetReadChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public async Task GetReadChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { ThrowIfDisposed(); - var server = await _cluster.SelectServerAndPinIfNeededAsync(_session, _serverSelector, deprioritizedServers, cancellationToken).ConfigureAwait(false); + var server = await _cluster.SelectServerAndPinIfNeededAsync(operationContext, _session, _serverSelector, deprioritizedServers).ConfigureAwait(false); return GetChannelSourceHelper(server); } diff --git a/src/MongoDB.Driver/Core/Bindings/ReadWriteBindingHandle.cs b/src/MongoDB.Driver/Core/Bindings/ReadWriteBindingHandle.cs index 7a298b3af60..0409d3ae844 100644 --- a/src/MongoDB.Driver/Core/Bindings/ReadWriteBindingHandle.cs +++ b/src/MongoDB.Driver/Core/Bindings/ReadWriteBindingHandle.cs @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -47,76 +46,76 @@ public ICoreSessionHandle Session get { return _reference.Instance.Session; } } - public IChannelSourceHandle GetReadChannelSource(CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext) { ThrowIfDisposed(); - return _reference.Instance.GetReadChannelSource(cancellationToken); + return _reference.Instance.GetReadChannelSource(operationContext); } - public Task GetReadChannelSourceAsync(CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext) { ThrowIfDisposed(); - return _reference.Instance.GetReadChannelSourceAsync(cancellationToken); + return _reference.Instance.GetReadChannelSourceAsync(operationContext); } - public IChannelSourceHandle GetReadChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { ThrowIfDisposed(); - return _reference.Instance.GetReadChannelSource(deprioritizedServers, cancellationToken); + return _reference.Instance.GetReadChannelSource(operationContext, deprioritizedServers); } - public Task GetReadChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { ThrowIfDisposed(); - return _reference.Instance.GetReadChannelSourceAsync(deprioritizedServers, cancellationToken); + return _reference.Instance.GetReadChannelSourceAsync(operationContext, deprioritizedServers); } - public IChannelSourceHandle GetWriteChannelSource(CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext) { ThrowIfDisposed(); - return _reference.Instance.GetWriteChannelSource(cancellationToken); + return _reference.Instance.GetWriteChannelSource(operationContext); } - public IChannelSourceHandle GetWriteChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { ThrowIfDisposed(); - return _reference.Instance.GetWriteChannelSource(deprioritizedServers, cancellationToken); + return _reference.Instance.GetWriteChannelSource(operationContext, deprioritizedServers); } - public IChannelSourceHandle GetWriteChannelSource(IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IMayUseSecondaryCriteria mayUseSecondary) { ThrowIfDisposed(); - return _reference.Instance.GetWriteChannelSource(mayUseSecondary, cancellationToken); + return _reference.Instance.GetWriteChannelSource(operationContext, mayUseSecondary); } - public IChannelSourceHandle GetWriteChannelSource(IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary) { ThrowIfDisposed(); - return _reference.Instance.GetWriteChannelSource(deprioritizedServers, mayUseSecondary, cancellationToken); + return _reference.Instance.GetWriteChannelSource(operationContext, deprioritizedServers, mayUseSecondary); } - public Task GetWriteChannelSourceAsync(CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext) { ThrowIfDisposed(); - return _reference.Instance.GetWriteChannelSourceAsync(cancellationToken); + return _reference.Instance.GetWriteChannelSourceAsync(operationContext); } - public Task GetWriteChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { ThrowIfDisposed(); - return _reference.Instance.GetWriteChannelSourceAsync(deprioritizedServers, cancellationToken); + return _reference.Instance.GetWriteChannelSourceAsync(operationContext, deprioritizedServers); } - public Task GetWriteChannelSourceAsync(IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext, IMayUseSecondaryCriteria mayUseSecondary) { ThrowIfDisposed(); - return _reference.Instance.GetWriteChannelSourceAsync(mayUseSecondary, cancellationToken); + return _reference.Instance.GetWriteChannelSourceAsync(operationContext, mayUseSecondary); } - public Task GetWriteChannelSourceAsync(IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary) { ThrowIfDisposed(); - return _reference.Instance.GetWriteChannelSourceAsync(deprioritizedServers, mayUseSecondary, cancellationToken); + return _reference.Instance.GetWriteChannelSourceAsync(operationContext, deprioritizedServers, mayUseSecondary); } public void Dispose() diff --git a/src/MongoDB.Driver/Core/Bindings/ServerChannelSource.cs b/src/MongoDB.Driver/Core/Bindings/ServerChannelSource.cs index a4cadb0001f..c5fbc55cea1 100644 --- a/src/MongoDB.Driver/Core/Bindings/ServerChannelSource.cs +++ b/src/MongoDB.Driver/Core/Bindings/ServerChannelSource.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -61,16 +60,16 @@ public void Dispose() } } - public IChannelHandle GetChannel(CancellationToken cancellationToken) + public IChannelHandle GetChannel(OperationContext operationContext) { ThrowIfDisposed(); - return _server.GetChannel(cancellationToken); + return _server.GetChannel(operationContext); } - public Task GetChannelAsync(CancellationToken cancellationToken) + public Task GetChannelAsync(OperationContext operationContext) { ThrowIfDisposed(); - return _server.GetChannelAsync(cancellationToken); + return _server.GetChannelAsync(operationContext); } private void ThrowIfDisposed() diff --git a/src/MongoDB.Driver/Core/Bindings/SingleServerReadBinding.cs b/src/MongoDB.Driver/Core/Bindings/SingleServerReadBinding.cs index 6bd5b858021..04a65fbd4b3 100644 --- a/src/MongoDB.Driver/Core/Bindings/SingleServerReadBinding.cs +++ b/src/MongoDB.Driver/Core/Bindings/SingleServerReadBinding.cs @@ -48,26 +48,26 @@ public ICoreSessionHandle Session get { return _session; } } - public IChannelSourceHandle GetReadChannelSource(CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext) { ThrowIfDisposed(); return GetChannelSourceHelper(); } - public Task GetReadChannelSourceAsync(CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext) { ThrowIfDisposed(); return Task.FromResult(GetChannelSourceHelper()); } - public IChannelSourceHandle GetReadChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetReadChannelSource(cancellationToken); + return GetReadChannelSource(operationContext); } - public Task GetReadChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetReadChannelSourceAsync(cancellationToken); + return GetReadChannelSourceAsync(operationContext); } public void Dispose() diff --git a/src/MongoDB.Driver/Core/Bindings/SingleServerReadWriteBinding.cs b/src/MongoDB.Driver/Core/Bindings/SingleServerReadWriteBinding.cs index 4f7756a1d7d..5113baa09c0 100644 --- a/src/MongoDB.Driver/Core/Bindings/SingleServerReadWriteBinding.cs +++ b/src/MongoDB.Driver/Core/Bindings/SingleServerReadWriteBinding.cs @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -53,68 +52,68 @@ public void Dispose() } } - public IChannelSourceHandle GetReadChannelSource(CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext) { ThrowIfDisposed(); return GetChannelSourceHelper(); } - public Task GetReadChannelSourceAsync(CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext) { ThrowIfDisposed(); return Task.FromResult(GetChannelSourceHelper()); } - public IChannelSourceHandle GetReadChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetReadChannelSource(cancellationToken); + return GetReadChannelSource(operationContext); } - public Task GetReadChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetReadChannelSourceAsync(cancellationToken); + return GetReadChannelSourceAsync(operationContext); } - public IChannelSourceHandle GetWriteChannelSource(CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext) { ThrowIfDisposed(); return GetChannelSourceHelper(); } - public IChannelSourceHandle GetWriteChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetWriteChannelSource(cancellationToken); + return GetWriteChannelSource(operationContext); } - public IChannelSourceHandle GetWriteChannelSource(IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IMayUseSecondaryCriteria mayUseSecondary) { - return GetWriteChannelSource(cancellationToken); // ignore mayUseSecondary + return GetWriteChannelSource(operationContext); // ignore mayUseSecondary } - public IChannelSourceHandle GetWriteChannelSource(IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary) { - return GetWriteChannelSource(mayUseSecondary, cancellationToken); + return GetWriteChannelSource(operationContext, mayUseSecondary); } - public Task GetWriteChannelSourceAsync(CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext) { ThrowIfDisposed(); return Task.FromResult(GetChannelSourceHelper()); } - public Task GetWriteChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { - return GetWriteChannelSourceAsync(cancellationToken); + return GetWriteChannelSourceAsync(operationContext); } - public Task GetWriteChannelSourceAsync(IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext, IMayUseSecondaryCriteria mayUseSecondary) { - return GetWriteChannelSourceAsync(cancellationToken); // ignore mayUseSecondary + return GetWriteChannelSourceAsync(operationContext); // ignore mayUseSecondary } - public Task GetWriteChannelSourceAsync(IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary) { - return GetWriteChannelSourceAsync(mayUseSecondary, cancellationToken); + return GetWriteChannelSourceAsync(operationContext, mayUseSecondary); } private IChannelSourceHandle GetChannelSourceHelper() diff --git a/src/MongoDB.Driver/Core/Bindings/WritableServerBinding.cs b/src/MongoDB.Driver/Core/Bindings/WritableServerBinding.cs index 27ea948577e..764bdc0e0ae 100644 --- a/src/MongoDB.Driver/Core/Bindings/WritableServerBinding.cs +++ b/src/MongoDB.Driver/Core/Bindings/WritableServerBinding.cs @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Clusters.ServerSelectors; @@ -48,48 +47,48 @@ public ICoreSessionHandle Session get { return _session; } } - public IChannelSourceHandle GetReadChannelSource(CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext) { - return GetReadChannelSource(null, cancellationToken); + return GetReadChannelSource(operationContext, null); } - public Task GetReadChannelSourceAsync(CancellationToken cancellationToken) + public Task GetReadChannelSourceAsync(OperationContext operationContext) { - return GetReadChannelSourceAsync(null, cancellationToken); + return GetReadChannelSourceAsync(operationContext, null); } - public IChannelSourceHandle GetReadChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public IChannelSourceHandle GetReadChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { ThrowIfDisposed(); - var server = _cluster.SelectServerAndPinIfNeeded(_session, WritableServerSelector.Instance, deprioritizedServers, cancellationToken); + var server = _cluster.SelectServerAndPinIfNeeded(operationContext, _session, WritableServerSelector.Instance, deprioritizedServers); return CreateServerChannelSource(server); } - public async Task GetReadChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public async Task GetReadChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { ThrowIfDisposed(); - var server = await _cluster.SelectServerAndPinIfNeededAsync(_session, WritableServerSelector.Instance, deprioritizedServers, cancellationToken).ConfigureAwait(false); + var server = await _cluster.SelectServerAndPinIfNeededAsync(operationContext, _session, WritableServerSelector.Instance, deprioritizedServers).ConfigureAwait(false); return CreateServerChannelSource(server); } - public IChannelSourceHandle GetWriteChannelSource(CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext) { - return GetWriteChannelSource(deprioritizedServers: null, cancellationToken); + return GetWriteChannelSource(operationContext, deprioritizedServers: null); } - public IChannelSourceHandle GetWriteChannelSource(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { ThrowIfDisposed(); - var server = _cluster.SelectServerAndPinIfNeeded(_session, WritableServerSelector.Instance, deprioritizedServers, cancellationToken); + var server = _cluster.SelectServerAndPinIfNeeded(operationContext, _session, WritableServerSelector.Instance, deprioritizedServers); return CreateServerChannelSource(server); } - public IChannelSourceHandle GetWriteChannelSource(IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IMayUseSecondaryCriteria mayUseSecondary) { - return GetWriteChannelSource(null, mayUseSecondary, cancellationToken); + return GetWriteChannelSource(operationContext, null, mayUseSecondary); } - public IChannelSourceHandle GetWriteChannelSource(IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public IChannelSourceHandle GetWriteChannelSource(OperationContext operationContext, IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary) { if (IsSessionPinnedToServer()) { @@ -102,28 +101,28 @@ public IChannelSourceHandle GetWriteChannelSource(IReadOnlyCollection GetWriteChannelSourceAsync(CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext) { - return GetWriteChannelSourceAsync(deprioritizedServers: null, cancellationToken); + return GetWriteChannelSourceAsync(operationContext, deprioritizedServers: null); } - public async Task GetWriteChannelSourceAsync(IReadOnlyCollection deprioritizedServers, CancellationToken cancellationToken) + public async Task GetWriteChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers) { ThrowIfDisposed(); - var server = await _cluster.SelectServerAndPinIfNeededAsync(_session, WritableServerSelector.Instance, deprioritizedServers, cancellationToken).ConfigureAwait(false); + var server = await _cluster.SelectServerAndPinIfNeededAsync(operationContext, _session, WritableServerSelector.Instance, deprioritizedServers).ConfigureAwait(false); return CreateServerChannelSource(server); } - public Task GetWriteChannelSourceAsync(IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public Task GetWriteChannelSourceAsync(OperationContext operationContext, IMayUseSecondaryCriteria mayUseSecondary) { - return GetWriteChannelSourceAsync(null, mayUseSecondary, cancellationToken); + return GetWriteChannelSourceAsync(operationContext, null, mayUseSecondary); } - public async Task GetWriteChannelSourceAsync(IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary, CancellationToken cancellationToken) + public async Task GetWriteChannelSourceAsync(OperationContext operationContext, IReadOnlyCollection deprioritizedServers, IMayUseSecondaryCriteria mayUseSecondary) { if (IsSessionPinnedToServer()) { @@ -136,7 +135,7 @@ public async Task GetWriteChannelSourceAsync(IReadOnlyColl ? new CompositeServerSelector(new IServerSelector[] { new PriorityServerSelector(deprioritizedServers), writableServerSelector }) : writableServerSelector; - var server = await _cluster.SelectServerAsync(selector, cancellationToken).ConfigureAwait(false); + var server = await _cluster.SelectServerAsync(operationContext, selector).ConfigureAwait(false); return CreateServerChannelSource(server); } diff --git a/src/MongoDB.Driver/Core/Clusters/Cluster.cs b/src/MongoDB.Driver/Core/Clusters/Cluster.cs index f1031387530..fbf9cfe7ac5 100644 --- a/src/MongoDB.Driver/Core/Clusters/Cluster.cs +++ b/src/MongoDB.Driver/Core/Clusters/Cluster.cs @@ -217,10 +217,13 @@ protected void OnDescriptionChanged(ClusterDescription oldDescription, ClusterDe DescriptionChanged?.Invoke(this, new ClusterDescriptionChangedEventArgs(oldDescription, newDescription)); } - public IServer SelectServer(IServerSelector selector, CancellationToken cancellationToken) + public IServer SelectServer(OperationContext operationContext, IServerSelector selector) { ThrowIfDisposedOrNotOpen(); Ensure.IsNotNull(selector, nameof(selector)); + Ensure.IsNotNull(operationContext, nameof(operationContext)); + + var serverSelectionOperationContext = operationContext.WithTimeout(Settings.ServerSelectionTimeout); using (var helper = new SelectServerHelper(this, selector)) { @@ -228,16 +231,23 @@ public IServer SelectServer(IServerSelector selector, CancellationToken cancella { while (true) { - var server = helper.SelectServer(); + var server = helper.SelectServer(serverSelectionOperationContext); if (server != null) { return server; } - helper.WaitingForDescriptionToChange(); - WaitForDescriptionChanged(helper.Selector, helper.Description, helper.DescriptionChangedTask, helper.TimeoutRemaining, cancellationToken); + helper.WaitForDescriptionChanged(serverSelectionOperationContext); } } + catch (TimeoutException) + { + var message = BuildTimeoutExceptionMessage(_settings.ServerSelectionTimeout, selector, helper.Description); + var timeoutException = new TimeoutException(message); + helper.HandleException(timeoutException); + + throw timeoutException; + } catch (Exception ex) { helper.HandleException(ex); @@ -246,10 +256,13 @@ public IServer SelectServer(IServerSelector selector, CancellationToken cancella } } - public async Task SelectServerAsync(IServerSelector selector, CancellationToken cancellationToken) + public async Task SelectServerAsync(OperationContext operationContext, IServerSelector selector) { ThrowIfDisposedOrNotOpen(); Ensure.IsNotNull(selector, nameof(selector)); + Ensure.IsNotNull(operationContext, nameof(operationContext)); + + var serverSelectionOperationContext = operationContext.WithTimeout(Settings.ServerSelectionTimeout); using (var helper = new SelectServerHelper(this, selector)) { @@ -257,16 +270,23 @@ public async Task SelectServerAsync(IServerSelector selector, Cancellat { while (true) { - var server = helper.SelectServer(); + var server = helper.SelectServer(serverSelectionOperationContext); if (server != null) { return server; } - helper.WaitingForDescriptionToChange(); - await WaitForDescriptionChangedAsync(helper.Selector, helper.Description, helper.DescriptionChangedTask, helper.TimeoutRemaining, cancellationToken).ConfigureAwait(false); + await helper.WaitForDescriptionChangedAsync(serverSelectionOperationContext).ConfigureAwait(false); } } + catch (TimeoutException) + { + var message = BuildTimeoutExceptionMessage(_settings.ServerSelectionTimeout, selector, helper.Description); + var timeoutException = new TimeoutException(message); + helper.HandleException(timeoutException); + + throw timeoutException; + } catch (Exception ex) { helper.HandleException(ex); @@ -320,30 +340,6 @@ private void ThrowIfDisposedOrNotOpen() } } - private void WaitForDescriptionChanged(IServerSelector selector, ClusterDescription description, Task descriptionChangedTask, TimeSpan timeout, CancellationToken cancellationToken) - { - using (var helper = new WaitForDescriptionChangedHelper(this, selector, description, descriptionChangedTask, timeout, cancellationToken)) - { - var index = Task.WaitAny(helper.Tasks); - helper.HandleCompletedTask(helper.Tasks[index]); - } - } - - private async Task WaitForDescriptionChangedAsync(IServerSelector selector, ClusterDescription description, Task descriptionChangedTask, TimeSpan timeout, CancellationToken cancellationToken) - { - using (var helper = new WaitForDescriptionChangedHelper(this, selector, description, descriptionChangedTask, timeout, cancellationToken)) - { - var completedTask = await Task.WhenAny(helper.Tasks).ConfigureAwait(false); - helper.HandleCompletedTask(completedTask); - } - } - - private void ThrowTimeoutException(IServerSelector selector, ClusterDescription description) - { - var message = BuildTimeoutExceptionMessage(_settings.ServerSelectionTimeout, selector, description); - throw new TimeoutException(message); - } - // nested classes internal sealed class ClusterDescriptionChangeSource { @@ -375,19 +371,16 @@ private class SelectServerHelper : IDisposable private readonly IServerSelector _selector; private readonly OperationsCountServerSelector _operationCountServerSelector; private readonly Stopwatch _stopwatch; - private readonly DateTime _timeoutAt; public SelectServerHelper(Cluster cluster, IServerSelector selector) { _cluster = cluster; - _connectedServers = new List(_cluster._descriptionWithChangedTaskCompletionSource.ClusterDescription?.Servers?.Count ?? 1); _connectedServerDescriptions = new List(_connectedServers.Count); _operationCountServerSelector = new OperationsCountServerSelector(_connectedServers); - _selector = DecorateSelector(selector); _stopwatch = Stopwatch.StartNew(); - _timeoutAt = DateTime.UtcNow + _cluster.Settings.ServerSelectionTimeout; + _selector = DecorateSelector(selector); } public ClusterDescription Description @@ -405,11 +398,6 @@ public IServerSelector Selector get { return _selector; } } - public TimeSpan TimeoutRemaining - { - get { return _timeoutAt - DateTime.UtcNow; } - } - public void Dispose() { if (_serverSelectionWaitQueueEntered) @@ -428,7 +416,7 @@ public void HandleException(Exception exception) EventContext.OperationName)); } - public IServer SelectServer() + public IServer SelectServer(OperationContext operationContext) { var clusterDescription = _cluster._descriptionWithChangedTaskCompletionSource; _descriptionChangedTask = clusterDescription.Changed; @@ -490,19 +478,27 @@ public IServer SelectServer() return selectedServer; } - public void WaitingForDescriptionToChange() + public void WaitForDescriptionChanged(OperationContext operationContext) { - if (!_serverSelectionWaitQueueEntered) - { - _cluster.EnterServerSelectionWaitQueue(_selector, _description, EventContext.OperationId, _timeoutAt - DateTime.UtcNow); - _serverSelectionWaitQueueEntered = true; - } + EnsureEnteredServerSelectionQueue(operationContext); + operationContext.WaitTask(DescriptionChangedTask); + } - var timeoutRemaining = _timeoutAt - DateTime.UtcNow; - if (timeoutRemaining <= TimeSpan.Zero) + public Task WaitForDescriptionChangedAsync(OperationContext operationContext) + { + EnsureEnteredServerSelectionQueue(operationContext); + return operationContext.WaitTaskAsync(DescriptionChangedTask); + } + + private void EnsureEnteredServerSelectionQueue(OperationContext operationContext) + { + if (_serverSelectionWaitQueueEntered) { - _cluster.ThrowTimeoutException(_selector, _description); + return; } + + _cluster.EnterServerSelectionWaitQueue(_selector, _description, EventContext.OperationId, operationContext.RemainingTimeout); + _serverSelectionWaitQueueEntered = true; } private IServerSelector DecorateSelector(IServerSelector selector) @@ -529,67 +525,6 @@ private IServerSelector DecorateSelector(IServerSelector selector) } } - private sealed class WaitForDescriptionChangedHelper : IDisposable - { - private readonly CancellationToken _cancellationToken; - private readonly TaskCompletionSource _cancellationTaskCompletionSource; - private readonly CancellationTokenRegistration _cancellationTokenRegistration; - private readonly Cluster _cluster; - private readonly ClusterDescription _description; - private readonly Task _descriptionChangedTask; - private readonly IServerSelector _selector; - private readonly CancellationTokenSource _timeoutCancellationTokenSource; - private readonly Task _timeoutTask; - - public WaitForDescriptionChangedHelper(Cluster cluster, IServerSelector selector, ClusterDescription description, Task descriptionChangedTask, TimeSpan timeout, CancellationToken cancellationToken) - { - _cluster = cluster; - _description = description; - _selector = selector; - _descriptionChangedTask = descriptionChangedTask; - _cancellationToken = cancellationToken; - _cancellationTaskCompletionSource = new TaskCompletionSource(); - _cancellationTokenRegistration = cancellationToken.Register(() => _cancellationTaskCompletionSource.TrySetCanceled()); - _timeoutCancellationTokenSource = new CancellationTokenSource(); - _timeoutTask = Task.Delay(timeout, _timeoutCancellationTokenSource.Token); - } - - public Task[] Tasks - { - get - { - return new Task[] - { - _descriptionChangedTask, - _timeoutTask, - _cancellationTaskCompletionSource.Task - }; - } - } - - public void Dispose() - { - _cancellationTokenRegistration.Dispose(); - _timeoutCancellationTokenSource.Dispose(); - } - - public void HandleCompletedTask(Task completedTask) - { - if (completedTask == _timeoutTask) - { - _cluster.ThrowTimeoutException(_selector, _description); - } - _timeoutCancellationTokenSource.Cancel(); - - if (completedTask == _cancellationTaskCompletionSource.Task) - { - _cancellationToken.ThrowIfCancellationRequested(); - } - - _descriptionChangedTask.GetAwaiter().GetResult(); // propagate exceptions - } - } - private static class State { public const int Initial = 0; diff --git a/src/MongoDB.Driver/Core/Clusters/ICluster.cs b/src/MongoDB.Driver/Core/Clusters/ICluster.cs index fcc817ec844..ea31d13bc12 100644 --- a/src/MongoDB.Driver/Core/Clusters/ICluster.cs +++ b/src/MongoDB.Driver/Core/Clusters/ICluster.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters.ServerSelectors; @@ -62,8 +61,8 @@ internal interface IClusterInternal : ICluster void Initialize(); - IServer SelectServer(IServerSelector selector, CancellationToken cancellationToken); - Task SelectServerAsync(IServerSelector selector, CancellationToken cancellationToken); + IServer SelectServer(OperationContext operationContext, IServerSelector selector); + Task SelectServerAsync(OperationContext operationContext, IServerSelector selector); ICoreSessionHandle StartSession(CoreSessionOptions options = null); } diff --git a/src/MongoDB.Driver/Core/Clusters/IClusterExtensions.cs b/src/MongoDB.Driver/Core/Clusters/IClusterExtensions.cs index 245ba8a48a3..e8060a75a07 100644 --- a/src/MongoDB.Driver/Core/Clusters/IClusterExtensions.cs +++ b/src/MongoDB.Driver/Core/Clusters/IClusterExtensions.cs @@ -14,7 +14,6 @@ */ using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters.ServerSelectors; @@ -26,10 +25,10 @@ internal static class IClusterExtensions { public static IServer SelectServerAndPinIfNeeded( this IClusterInternal cluster, + OperationContext operationContext, ICoreSessionHandle session, IServerSelector selector, - IReadOnlyCollection deprioritizedServers, - CancellationToken cancellationToken) + IReadOnlyCollection deprioritizedServers) { var pinnedServer = GetPinnedServerIfValid(cluster, session); if (pinnedServer != null) @@ -41,19 +40,19 @@ public static IServer SelectServerAndPinIfNeeded( ? new CompositeServerSelector(new[] { new PriorityServerSelector(deprioritizedServers), selector }) : selector; - // Server selection also updates the cluster type, allowing us to to determine if the server + // Server selection also updates the cluster type, allowing us to determine if the server // should be pinned. - var server = cluster.SelectServer(selector, cancellationToken); + var server = cluster.SelectServer(operationContext, selector); PinServerIfNeeded(cluster, session, server); return server; } public static async Task SelectServerAndPinIfNeededAsync( this IClusterInternal cluster, + OperationContext operationContext, ICoreSessionHandle session, IServerSelector selector, - IReadOnlyCollection deprioritizedServers, - CancellationToken cancellationToken) + IReadOnlyCollection deprioritizedServers) { var pinnedServer = GetPinnedServerIfValid(cluster, session); if (pinnedServer != null) @@ -65,9 +64,9 @@ public static async Task SelectServerAndPinIfNeededAsync( ? new CompositeServerSelector(new[] { new PriorityServerSelector(deprioritizedServers), selector }) : selector; - // Server selection also updates the cluster type, allowing us to to determine if the server + // Server selection also updates the cluster type, allowing us to determine if the server // should be pinned. - var server = await cluster.SelectServerAsync(selector, cancellationToken).ConfigureAwait(false); + var server = await cluster.SelectServerAsync(operationContext, selector).ConfigureAwait(false); PinServerIfNeeded(cluster, session, server); return server; diff --git a/src/MongoDB.Driver/Core/Clusters/LoadBalancedCluster.cs b/src/MongoDB.Driver/Core/Clusters/LoadBalancedCluster.cs index 712d9f52fc7..c77d2d45241 100644 --- a/src/MongoDB.Driver/Core/Clusters/LoadBalancedCluster.cs +++ b/src/MongoDB.Driver/Core/Clusters/LoadBalancedCluster.cs @@ -170,30 +170,39 @@ public void Initialize() } } - public IServer SelectServer(IServerSelector selector, CancellationToken cancellationToken) + public IServer SelectServer(OperationContext operationContext, IServerSelector selector) { + Ensure.IsNotNull(selector, nameof(selector)); + Ensure.IsNotNull(operationContext, nameof(operationContext)); ThrowIfDisposed(); + var serverSelectionOperationContext = operationContext.WithTimeout(_settings.ServerSelectionTimeout); + _serverSelectionEventLogger.LogAndPublish(new ClusterSelectingServerEvent( _description, selector, null, EventContext.OperationName)); - var index = Task.WaitAny(new[] { _serverReadyTaskCompletionSource.Task }, (int)_settings.ServerSelectionTimeout.TotalMilliseconds, cancellationToken); - if (index != 0) + var stopwatch = Stopwatch.StartNew(); + try + { + serverSelectionOperationContext.WaitTask(_serverReadyTaskCompletionSource.Task); + } + catch (TimeoutException) { - cancellationToken.ThrowIfCancellationRequested(); throw CreateTimeoutException(_description); // _description will contain dnsException } if (_server != null) { + stopwatch.Stop(); + _serverSelectionEventLogger.LogAndPublish(new ClusterSelectedServerEvent( _description, selector, _server.Description, - TimeSpan.FromSeconds(1), + stopwatch.Elapsed, null, EventContext.OperationName)); } @@ -202,31 +211,38 @@ public IServer SelectServer(IServerSelector selector, CancellationToken cancella throw new InvalidOperationException("The server must be created before usage."); // should not be reached } - public async Task SelectServerAsync(IServerSelector selector, CancellationToken cancellationToken) + public async Task SelectServerAsync(OperationContext operationContext, IServerSelector selector) { + Ensure.IsNotNull(selector, nameof(selector)); + Ensure.IsNotNull(operationContext, nameof(operationContext)); ThrowIfDisposed(); + var serverSelectionOperationContext = operationContext.WithTimeout(_settings.ServerSelectionTimeout); + _serverSelectionEventLogger.LogAndPublish(new ClusterSelectingServerEvent( _description, selector, null, EventContext.OperationName)); - var timeoutTask = Task.Delay(_settings.ServerSelectionTimeout, cancellationToken); - var triggeredTask = await Task.WhenAny(_serverReadyTaskCompletionSource.Task, timeoutTask).ConfigureAwait(false); - if (triggeredTask == timeoutTask) + var stopwatch = Stopwatch.StartNew(); + try + { + await serverSelectionOperationContext.WaitTaskAsync(_serverReadyTaskCompletionSource.Task).ConfigureAwait(false); + } + catch (TimeoutException) { - cancellationToken.ThrowIfCancellationRequested(); throw CreateTimeoutException(_description); // _description will contain dnsException } if (_server != null) { + stopwatch.Stop(); _serverSelectionEventLogger.LogAndPublish(new ClusterSelectedServerEvent( _description, selector, _server.Description, - TimeSpan.FromSeconds(1), + stopwatch.Elapsed, null, EventContext.OperationName)); } diff --git a/src/MongoDB.Driver/Core/ConnectionPools/ExclusiveConnectionPool.Helpers.cs b/src/MongoDB.Driver/Core/ConnectionPools/ExclusiveConnectionPool.Helpers.cs index 77895ed99a4..b4adb83a8db 100644 --- a/src/MongoDB.Driver/Core/ConnectionPools/ExclusiveConnectionPool.Helpers.cs +++ b/src/MongoDB.Driver/Core/ConnectionPools/ExclusiveConnectionPool.Helpers.cs @@ -33,7 +33,7 @@ namespace MongoDB.Driver.Core.ConnectionPools internal sealed partial class ExclusiveConnectionPool { // private methods - private Exception CreateTimeoutException(Stopwatch stopwatch, string message) + private Exception CreateTimeoutException(TimeSpan elapsed, string message) { var checkOutsForCursorCount = _checkOutReasonCounter.GetCheckOutsCount(CheckOutReason.Cursor); var checkOutsForTransactionCount = _checkOutReasonCounter.GetCheckOutsCount(CheckOutReason.Transaction); @@ -47,7 +47,7 @@ private Exception CreateTimeoutException(Stopwatch stopwatch, string message) var checkOutsForOtherCount = checkOutsCount - checkOutsForCursorCount - checkOutsForTransactionCount; message = - $"Timed out after {stopwatch.ElapsedMilliseconds}ms waiting for a connection from the connection pool. " + + $"Timed out after {elapsed.TotalMilliseconds}ms waiting for a connection from the connection pool. " + $"maxPoolSize: {maxPoolSize}, " + $"connections in use by cursors: {checkOutsForCursorCount}, " + $"connections in use by transactions: {checkOutsForTransactionCount}, " + @@ -166,7 +166,6 @@ private sealed class AcquireConnectionHelper : IDisposable { // private fields private readonly ExclusiveConnectionPool _pool; - private readonly TimeSpan _timeout; private bool _enteredWaitQueue; private SemaphoreSlimSignalable.SemaphoreWaitResult _poolQueueWaitResult; @@ -175,32 +174,31 @@ private sealed class AcquireConnectionHelper : IDisposable public AcquireConnectionHelper(ExclusiveConnectionPool pool) { _pool = pool; - _timeout = pool._settings.WaitQueueTimeout; } - public IConnectionHandle AcquireConnection(CancellationToken cancellationToken) + public IConnectionHandle AcquireConnection(OperationContext operationContext) { var stopwatch = new Stopwatch(); try { StartCheckingOut(stopwatch); - _poolQueueWaitResult = _pool._maxConnectionsQueue.WaitSignaled(_timeout, cancellationToken); + _poolQueueWaitResult = _pool._maxConnectionsQueue.WaitSignaled(operationContext.RemainingTimeout, operationContext.CancellationToken); if (_poolQueueWaitResult == SemaphoreSlimSignalable.SemaphoreWaitResult.Entered) { PooledConnection pooledConnection; - var timeout = EnsureTimeout(stopwatch); + ThrowIfTimedOut(operationContext, stopwatch); - using (var connectionCreator = new ConnectionCreator(_pool, timeout)) + using (var connectionCreator = new ConnectionCreator(_pool)) { - pooledConnection = connectionCreator.CreateOpenedOrReuse(cancellationToken); + pooledConnection = connectionCreator.CreateOpenedOrReuse(operationContext); } return EndCheckingOut(pooledConnection, stopwatch); } stopwatch.Stop(); - throw CreateException(stopwatch); + throw CreateException(stopwatch.Elapsed); } catch (Exception ex) { @@ -210,29 +208,29 @@ public IConnectionHandle AcquireConnection(CancellationToken cancellationToken) } } - public async Task AcquireConnectionAsync(CancellationToken cancellationToken) + public async Task AcquireConnectionAsync(OperationContext operationContext) { var stopwatch = new Stopwatch(); try { StartCheckingOut(stopwatch); - _poolQueueWaitResult = await _pool._maxConnectionsQueue.WaitSignaledAsync(_timeout, cancellationToken).ConfigureAwait(false); + _poolQueueWaitResult = await _pool._maxConnectionsQueue.WaitSignaledAsync(operationContext.RemainingTimeout, operationContext.CancellationToken).ConfigureAwait(false); if (_poolQueueWaitResult == SemaphoreSlimSignalable.SemaphoreWaitResult.Entered) { PooledConnection pooledConnection; - var timeout = EnsureTimeout(stopwatch); + ThrowIfTimedOut(operationContext, stopwatch); - using (var connectionCreator = new ConnectionCreator(_pool, timeout)) + using (var connectionCreator = new ConnectionCreator(_pool)) { - pooledConnection = await connectionCreator.CreateOpenedOrReuseAsync(cancellationToken).ConfigureAwait(false); + pooledConnection = await connectionCreator.CreateOpenedOrReuseAsync(operationContext).ConfigureAwait(false); } return EndCheckingOut(pooledConnection, stopwatch); } stopwatch.Stop(); - throw CreateException(stopwatch); + throw CreateException(stopwatch.Elapsed); } catch (Exception ex) { @@ -281,13 +279,21 @@ private void AcquireWaitQueueSlot() _enteredWaitQueue = true; } + private void ThrowIfTimedOut(OperationContext operationContext, Stopwatch stopwatch) + { + if (operationContext.IsTimedOut()) + { + stopwatch.Stop(); + throw _pool.CreateTimeoutException(stopwatch.Elapsed, $"Timed out waiting for a connection after {stopwatch.ElapsedMilliseconds}ms."); + } + } + private void StartCheckingOut(Stopwatch stopwatch) { _pool._eventLogger.LogAndPublish(new ConnectionPoolCheckingOutConnectionEvent(_pool._serverId, EventContext.OperationId)); - stopwatch.Start(); - _pool._poolState.ThrowIfNotReady(); + _pool._poolState.ThrowIfNotReady(); AcquireWaitQueueSlot(); } @@ -296,7 +302,6 @@ private IConnectionHandle EndCheckingOut(PooledConnection pooledConnection, Stop var reference = new ReferenceCounted(pooledConnection, _pool.ReleaseConnection); var connectionHandle = new AcquiredConnection(_pool, reference); - stopwatch.Stop(); _pool._eventLogger.LogAndPublish(new ConnectionPoolCheckedOutConnectionEvent(connectionHandle.ConnectionId, stopwatch.Elapsed, EventContext.OperationId)); // no need to release the semaphore @@ -305,26 +310,13 @@ private IConnectionHandle EndCheckingOut(PooledConnection pooledConnection, Stop return connectionHandle; } - private TimeSpan EnsureTimeout(Stopwatch stopwatch) - { - var timeSpentInWaitQueue = stopwatch.Elapsed; - var timeout = _timeout - timeSpentInWaitQueue; - - if (timeout < TimeSpan.Zero) - { - throw _pool.CreateTimeoutException(stopwatch, $"Timed out waiting for a connection after {timeSpentInWaitQueue.TotalMilliseconds}ms."); - } - - return timeout; - } - - private Exception CreateException(Stopwatch stopwatch) => + private Exception CreateException(TimeSpan elapsed) => _poolQueueWaitResult switch { SemaphoreSlimSignalable.SemaphoreWaitResult.Signaled => MongoConnectionPoolPausedException.ForConnectionPool(_pool._endPoint), SemaphoreSlimSignalable.SemaphoreWaitResult.TimedOut => - _pool.CreateTimeoutException(stopwatch, $"Timed out waiting for a connection after {stopwatch.ElapsedMilliseconds}ms."), + _pool.CreateTimeoutException(elapsed, $"Timed out waiting for a connection after {elapsed.TotalMilliseconds}ms."), // should not be reached _ => new InvalidOperationException($"Invalid {_poolQueueWaitResult}.") }; @@ -846,41 +838,35 @@ public void UntrackInUseConnection(PooledConnection connection) internal sealed class ConnectionCreator : IDisposable { private readonly ExclusiveConnectionPool _pool; - private readonly TimeSpan _connectingTimeout; private PooledConnection _connection; private bool _disposeConnection; private SemaphoreSlimSignalable.SemaphoreWaitResult _connectingWaitStatus; - private Stopwatch _stopwatch; - - public ConnectionCreator(ExclusiveConnectionPool pool, TimeSpan connectingTimeout) + public ConnectionCreator(ExclusiveConnectionPool pool) { _pool = pool; - _connectingTimeout = connectingTimeout; _connectingWaitStatus = SemaphoreSlimSignalable.SemaphoreWaitResult.None; _connection = null; _disposeConnection = true; - _stopwatch = null; } - public PooledConnection CreateOpened(CancellationToken cancellationToken) + public PooledConnection CreateOpened(TimeSpan maxConnectingQueueTimeout, CancellationToken cancellationToken) { try { var stopwatch = Stopwatch.StartNew(); - _connectingWaitStatus = _pool._maxConnectingQueue.Wait(_connectingTimeout, cancellationToken); + _connectingWaitStatus = _pool._maxConnectingQueue.Wait(maxConnectingQueueTimeout, cancellationToken); stopwatch.Stop(); - _pool._poolState.ThrowIfNotReady(); if (_connectingWaitStatus == SemaphoreSlimSignalable.SemaphoreWaitResult.TimedOut) { - _pool.CreateTimeoutException(stopwatch, $"Timed out waiting for in connecting queue after {stopwatch.ElapsedMilliseconds}ms."); + _pool.CreateTimeoutException(stopwatch.Elapsed, $"Timed out waiting for in connecting queue after {stopwatch.ElapsedMilliseconds}ms."); } - return CreateOpenedInternal(cancellationToken); + return CreateOpenedInternal(new(Timeout.InfiniteTimeSpan, cancellationToken)); } catch (Exception ex) { @@ -889,12 +875,11 @@ public PooledConnection CreateOpened(CancellationToken cancellationToken) } } - public PooledConnection CreateOpenedOrReuse(CancellationToken cancellationToken) + public PooledConnection CreateOpenedOrReuse(OperationContext operationContext) { try { var connection = _pool._connectionHolder.Acquire(); - var waitTimeout = _connectingTimeout; var stopwatch = Stopwatch.StartNew(); while (connection == null) @@ -905,21 +890,19 @@ public PooledConnection CreateOpenedOrReuse(CancellationToken cancellationToken) // Entered: The request was successfully fulfilled, and a connection establishment can start // Signaled: The request was interrupted because Connection was return to pool and can be reused // Timeout: The request was timed out after WaitQueueTimeout period. - _connectingWaitStatus = _pool._maxConnectingQueue.WaitSignaled(waitTimeout, cancellationToken); + _connectingWaitStatus = _pool._maxConnectingQueue.WaitSignaled(operationContext.RemainingTimeout, operationContext.CancellationToken); connection = _connectingWaitStatus switch { SemaphoreSlimSignalable.SemaphoreWaitResult.Signaled => _pool._connectionHolder.Acquire(), - SemaphoreSlimSignalable.SemaphoreWaitResult.Entered => CreateOpenedInternal(cancellationToken), - SemaphoreSlimSignalable.SemaphoreWaitResult.TimedOut => throw CreateTimeoutException(stopwatch), + SemaphoreSlimSignalable.SemaphoreWaitResult.Entered => CreateOpenedInternal(operationContext), + SemaphoreSlimSignalable.SemaphoreWaitResult.TimedOut => throw CreateTimeoutException(stopwatch.Elapsed), _ => throw new InvalidOperationException($"Invalid wait result {_connectingWaitStatus}") }; - waitTimeout = _connectingTimeout - stopwatch.Elapsed; - - if (connection == null && waitTimeout <= TimeSpan.Zero) + if (connection == null && operationContext.IsTimedOut()) { - throw CreateTimeoutException(stopwatch); + throw CreateTimeoutException(stopwatch.Elapsed); } } @@ -932,13 +915,11 @@ public PooledConnection CreateOpenedOrReuse(CancellationToken cancellationToken) } } - public async Task CreateOpenedOrReuseAsync(CancellationToken cancellationToken) + public async Task CreateOpenedOrReuseAsync(OperationContext operationContext) { try { var connection = _pool._connectionHolder.Acquire(); - - var waitTimeout = _connectingTimeout; var stopwatch = Stopwatch.StartNew(); while (connection == null) @@ -949,21 +930,19 @@ public async Task CreateOpenedOrReuseAsync(CancellationToken c // Entered: The request was successfully fulfilled, and a connection establishment can start // Signaled: The request was interrupted because Connection was return to pool and can be reused // Timeout: The request was timed out after WaitQueueTimeout period. - _connectingWaitStatus = await _pool._maxConnectingQueue.WaitSignaledAsync(waitTimeout, cancellationToken).ConfigureAwait(false); + _connectingWaitStatus = await _pool._maxConnectingQueue.WaitSignaledAsync(operationContext.RemainingTimeout, operationContext.CancellationToken).ConfigureAwait(false); connection = _connectingWaitStatus switch { SemaphoreSlimSignalable.SemaphoreWaitResult.Signaled => _pool._connectionHolder.Acquire(), - SemaphoreSlimSignalable.SemaphoreWaitResult.Entered => await CreateOpenedInternalAsync(cancellationToken).ConfigureAwait(false), - SemaphoreSlimSignalable.SemaphoreWaitResult.TimedOut => throw CreateTimeoutException(stopwatch), + SemaphoreSlimSignalable.SemaphoreWaitResult.Entered => await CreateOpenedInternalAsync(operationContext).ConfigureAwait(false), + SemaphoreSlimSignalable.SemaphoreWaitResult.TimedOut => throw CreateTimeoutException(stopwatch.Elapsed), _ => throw new InvalidOperationException($"Invalid wait result {_connectingWaitStatus}") }; - waitTimeout = _connectingTimeout - stopwatch.Elapsed; - - if (connection == null && waitTimeout <= TimeSpan.Zero) + if (connection == null && operationContext.IsTimedOut()) { - throw CreateTimeoutException(stopwatch); + throw CreateTimeoutException(stopwatch.Elapsed); } } @@ -991,54 +970,55 @@ public void Dispose() } // private methods - private PooledConnection CreateOpenedInternal(CancellationToken cancellationToken) + private PooledConnection CreateOpenedInternal(OperationContext operationContext) { - StartCreating(cancellationToken); + var stopwatch = StartCreating(operationContext); - _connection.Open(cancellationToken); + // TODO: CSOT add support of CSOT timeout in connection open code too. + _connection.Open(operationContext.CancellationToken); - FinishCreating(_connection.Description); + FinishCreating(_connection.Description, stopwatch); return _connection; } - private async Task CreateOpenedInternalAsync(CancellationToken cancellationToken) + private async Task CreateOpenedInternalAsync(OperationContext operationContext) { - StartCreating(cancellationToken); + var stopwatch = StartCreating(operationContext); - await _connection.OpenAsync(cancellationToken).ConfigureAwait(false); + // TODO: CSOT add support of CSOT timeout in connection open code too. + await _connection.OpenAsync(operationContext.CancellationToken).ConfigureAwait(false); - FinishCreating(_connection.Description); + FinishCreating(_connection.Description, stopwatch); return _connection; } - private void StartCreating(CancellationToken cancellationToken) + private Stopwatch StartCreating(OperationContext operationContext) { _pool._eventLogger.LogAndPublish(new ConnectionPoolAddingConnectionEvent(_pool._serverId, EventContext.OperationId)); - cancellationToken.ThrowIfCancellationRequested(); - - _stopwatch = Stopwatch.StartNew(); + operationContext.ThrowIfTimedOutOrCanceled(); + var stopwatch = Stopwatch.StartNew(); _connection = _pool.CreateNewConnection(); + return stopwatch; } - private void FinishCreating(ConnectionDescription description) + private void FinishCreating(ConnectionDescription description, Stopwatch stopwatch) { - _stopwatch.Stop(); - - _pool._eventLogger.LogAndPublish(new ConnectionPoolAddedConnectionEvent(_connection.ConnectionId, _stopwatch.Elapsed, EventContext.OperationId)); + stopwatch.Stop(); + _pool._eventLogger.LogAndPublish(new ConnectionPoolAddedConnectionEvent(_connection.ConnectionId, stopwatch.Elapsed, EventContext.OperationId)); // Only if reached this stage, connection should not be disposed _disposeConnection = false; _pool._serviceStates.IncrementConnectionCount(description?.ServiceId); } - private Exception CreateTimeoutException(Stopwatch stopwatch) + private Exception CreateTimeoutException(TimeSpan elapsed) { - var message = $"Timed out waiting in connecting queue after {stopwatch.ElapsedMilliseconds}ms."; - return _pool.CreateTimeoutException(stopwatch, message); + var message = $"Timed out waiting in connecting queue after {elapsed.TotalMilliseconds}ms."; + return _pool.CreateTimeoutException(elapsed, message); } } } diff --git a/src/MongoDB.Driver/Core/ConnectionPools/ExclusiveConnectionPool.cs b/src/MongoDB.Driver/Core/ConnectionPools/ExclusiveConnectionPool.cs index 8490bba1b07..7489d714081 100644 --- a/src/MongoDB.Driver/Core/ConnectionPools/ExclusiveConnectionPool.cs +++ b/src/MongoDB.Driver/Core/ConnectionPools/ExclusiveConnectionPool.cs @@ -15,7 +15,6 @@ using System; using System.Net; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Driver.Core.Configuration; @@ -140,16 +139,18 @@ public int UsedCount internal ListConnectionHolder ConnectionHolder => _connectionHolder; // public methods - public IConnectionHandle AcquireConnection(CancellationToken cancellationToken) + public IConnectionHandle AcquireConnection(OperationContext operationContext) { + operationContext = operationContext.WithTimeout(Settings.WaitQueueTimeout); using var helper = new AcquireConnectionHelper(this); - return helper.AcquireConnection(cancellationToken); + return helper.AcquireConnection(operationContext); } - public async Task AcquireConnectionAsync(CancellationToken cancellationToken) + public async Task AcquireConnectionAsync(OperationContext operationContext) { + operationContext = operationContext.WithTimeout(Settings.WaitQueueTimeout); using var helper = new AcquireConnectionHelper(this); - return await helper.AcquireConnectionAsync(cancellationToken).ConfigureAwait(false); + return await helper.AcquireConnectionAsync(operationContext).ConfigureAwait(false); } public void Clear(bool closeInUseConnections = false) diff --git a/src/MongoDB.Driver/Core/ConnectionPools/IConnectionPool.cs b/src/MongoDB.Driver/Core/ConnectionPools/IConnectionPool.cs index 24f2ff48e4e..599185c797a 100644 --- a/src/MongoDB.Driver/Core/ConnectionPools/IConnectionPool.cs +++ b/src/MongoDB.Driver/Core/ConnectionPools/IConnectionPool.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Driver.Core.Connections; @@ -27,8 +26,8 @@ internal interface IConnectionPool : IDisposable int Generation { get; } ServerId ServerId { get; } - IConnectionHandle AcquireConnection(CancellationToken cancellationToken); - Task AcquireConnectionAsync(CancellationToken cancellationToken); + IConnectionHandle AcquireConnection(OperationContext operationContext); + Task AcquireConnectionAsync(OperationContext operationContext); void Clear(bool closeInUseConnections = false); void Clear(ObjectId serviceId); int GetGeneration(ObjectId? serviceId); diff --git a/src/MongoDB.Driver/Core/ConnectionPools/MaintenanceHelper.cs b/src/MongoDB.Driver/Core/ConnectionPools/MaintenanceHelper.cs index a8f2bdfa12f..568ed1933c3 100644 --- a/src/MongoDB.Driver/Core/ConnectionPools/MaintenanceHelper.cs +++ b/src/MongoDB.Driver/Core/ConnectionPools/MaintenanceHelper.cs @@ -125,9 +125,9 @@ private void EnsureMinSize(CancellationToken cancellationToken) return; } - using (var connectionCreator = new ConnectionCreator(_connectionPool, minTimeout)) + using (var connectionCreator = new ConnectionCreator(_connectionPool)) { - var connection = connectionCreator.CreateOpened(cancellationToken); + var connection = connectionCreator.CreateOpened(minTimeout, cancellationToken); _connectionPool.ConnectionHolder.Return(connection); } } diff --git a/src/MongoDB.Driver/Core/Misc/Feature.cs b/src/MongoDB.Driver/Core/Misc/Feature.cs index 506e28de10c..762b7d0591e 100644 --- a/src/MongoDB.Driver/Core/Misc/Feature.cs +++ b/src/MongoDB.Driver/Core/Misc/Feature.cs @@ -565,9 +565,11 @@ internal int LastNotSupportedWireVersion public void ThrowIfNotSupported(IMongoClient client, CancellationToken cancellationToken = default) { var cluster = client.GetClusterInternal(); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); using (var binding = new ReadWriteBindingHandle(new WritableServerBinding(cluster, NoCoreSession.NewHandle()))) - using (var channelSource = binding.GetWriteChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetWriteChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) { // Use WireVersion from a connection since server level value may be null ThrowIfNotSupported(channel.ConnectionDescription.MaxWireVersion); @@ -582,9 +584,11 @@ public void ThrowIfNotSupported(IMongoClient client, CancellationToken cancellat public async Task ThrowIfNotSupportedAsync(IMongoClient client, CancellationToken cancellationToken = default) { var cluster = client.GetClusterInternal(); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); using (var binding = new ReadWriteBindingHandle(new WritableServerBinding(cluster, NoCoreSession.NewHandle()))) - using (var channelSource = await binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetWriteChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) { // Use WireVersion from a connection since server level value may be null ThrowIfNotSupported(channel.ConnectionDescription.MaxWireVersion); diff --git a/src/MongoDB.Driver/Core/Operations/AggregateOperation.cs b/src/MongoDB.Driver/Core/Operations/AggregateOperation.cs index a66161543a9..86eae237593 100644 --- a/src/MongoDB.Driver/Core/Operations/AggregateOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/AggregateOperation.cs @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.IO; @@ -270,19 +269,19 @@ public bool? UseCursor // methods /// - public IAsyncCursor Execute(IReadBinding binding, CancellationToken cancellationToken) + public IAsyncCursor Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var context = RetryableReadContext.Create(binding, _retryRequested, cancellationToken)) + using (var context = RetryableReadContext.Create(operationContext, binding, _retryRequested)) { - return Execute(context, cancellationToken); + return Execute(operationContext, context); } } /// - public IAsyncCursor Execute(RetryableReadContext context, CancellationToken cancellationToken) + public IAsyncCursor Execute(OperationContext operationContext, RetryableReadContext context) { Ensure.IsNotNull(context, nameof(context)); EnsureIsReadOnlyPipeline(); @@ -290,7 +289,7 @@ public IAsyncCursor Execute(RetryableReadContext context, CancellationT using (EventContext.BeginOperation()) { var operation = CreateOperation(context); - var result = operation.Execute(context, cancellationToken); + var result = operation.Execute(operationContext, context); context.ChannelSource.Session.SetSnapshotTimeIfNeeded(result.AtClusterTime); @@ -299,19 +298,19 @@ public IAsyncCursor Execute(RetryableReadContext context, CancellationT } /// - public async Task> ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public async Task> ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var context = await RetryableReadContext.CreateAsync(binding, _retryRequested, cancellationToken).ConfigureAwait(false)) + using (var context = await RetryableReadContext.CreateAsync(operationContext, binding, _retryRequested).ConfigureAwait(false)) { - return await ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + return await ExecuteAsync(operationContext, context).ConfigureAwait(false); } } /// - public async Task> ExecuteAsync(RetryableReadContext context, CancellationToken cancellationToken) + public async Task> ExecuteAsync(OperationContext operationContext, RetryableReadContext context) { Ensure.IsNotNull(context, nameof(context)); EnsureIsReadOnlyPipeline(); @@ -319,7 +318,7 @@ public async Task> ExecuteAsync(RetryableReadContext conte using (EventContext.BeginOperation()) { var operation = CreateOperation(context); - var result = await operation.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + var result = await operation.ExecuteAsync(operationContext, context).ConfigureAwait(false); context.ChannelSource.Session.SetSnapshotTimeIfNeeded(result.AtClusterTime); diff --git a/src/MongoDB.Driver/Core/Operations/AggregateToCollectionOperation.cs b/src/MongoDB.Driver/Core/Operations/AggregateToCollectionOperation.cs index 88bc86f854c..79c684d9752 100644 --- a/src/MongoDB.Driver/Core/Operations/AggregateToCollectionOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/AggregateToCollectionOperation.cs @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -148,33 +147,33 @@ public WriteConcern WriteConcern set { _writeConcern = value; } } - public BsonDocument Execute(IWriteBinding binding, CancellationToken cancellationToken) + public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); var mayUseSecondary = new MayUseSecondary(_readPreference); using (BeginOperation()) - using (var channelSource = binding.GetWriteChannelSource(mayUseSecondary, cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetWriteChannelSource(operationContext, mayUseSecondary)) + using (var channel = channelSource.GetChannel(operationContext)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session, channel.ConnectionDescription, mayUseSecondary.EffectiveReadPreference); - return operation.Execute(channelBinding, cancellationToken); + return operation.Execute(operationContext, channelBinding); } } - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); var mayUseSecondary = new MayUseSecondary(_readPreference); using (BeginOperation()) - using (var channelSource = await binding.GetWriteChannelSourceAsync(mayUseSecondary, cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetWriteChannelSourceAsync(operationContext, mayUseSecondary).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session, channel.ConnectionDescription, mayUseSecondary.EffectiveReadPreference); - return await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); } } diff --git a/src/MongoDB.Driver/Core/Operations/AsyncCursor.cs b/src/MongoDB.Driver/Core/Operations/AsyncCursor.cs index 1f0e4e46138..bb0b2daa92a 100644 --- a/src/MongoDB.Driver/Core/Operations/AsyncCursor.cs +++ b/src/MongoDB.Driver/Core/Operations/AsyncCursor.cs @@ -409,8 +409,10 @@ private void DisposeChannelSourceIfNoLongerNeeded() private CursorBatch GetNextBatch(CancellationToken cancellationToken) { + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); using (EventContext.BeginOperation(_operationId)) - using (var channel = _channelSource.GetChannel(cancellationToken)) + using (var channel = _channelSource.GetChannel(operationContext)) { return ExecuteGetMoreCommand(channel, cancellationToken); } @@ -418,8 +420,10 @@ private CursorBatch GetNextBatch(CancellationToken cancellationToken) private async Task> GetNextBatchAsync(CancellationToken cancellationToken) { + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); using (EventContext.BeginOperation(_operationId)) - using (var channel = await _channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channel = await _channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) { return await ExecuteGetMoreCommandAsync(channel, cancellationToken).ConfigureAwait(false); } @@ -432,10 +436,11 @@ private bool IsMongoCursorNotFoundException(MongoCommandException exception) private void KillCursors(CancellationToken cancellationToken) { + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); using (EventContext.BeginOperation(_operationId)) using (EventContext.BeginKillCursors(_collectionNamespace)) - using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10))) - using (var channel = _channelSource.GetChannel(cancellationTokenSource.Token)) + using (var channel = _channelSource.GetChannel(operationContext.WithTimeout(TimeSpan.FromSeconds(10)))) { if (!channel.Connection.IsExpired) { @@ -446,10 +451,11 @@ private void KillCursors(CancellationToken cancellationToken) private async Task KillCursorsAsync(CancellationToken cancellationToken) { + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); using (EventContext.BeginOperation(_operationId)) using (EventContext.BeginKillCursors(_collectionNamespace)) - using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10))) - using (var channel = await _channelSource.GetChannelAsync(cancellationTokenSource.Token).ConfigureAwait(false)) + using (var channel = await _channelSource.GetChannelAsync(operationContext.WithTimeout(TimeSpan.FromSeconds(10))).ConfigureAwait(false)) { if (!channel.Connection.IsExpired) { diff --git a/src/MongoDB.Driver/Core/Operations/BulkMixedWriteOperation.cs b/src/MongoDB.Driver/Core/Operations/BulkMixedWriteOperation.cs index c529c2ebc56..efe28a3f4af 100644 --- a/src/MongoDB.Driver/Core/Operations/BulkMixedWriteOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/BulkMixedWriteOperation.cs @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Driver.Core.Bindings; @@ -137,33 +136,33 @@ public WriteConcern WriteConcern set { _writeConcern = Ensure.IsNotNull(value, nameof(value)); } } - public BulkWriteOperationResult Execute(IWriteBinding binding, CancellationToken cancellationToken) + public BulkWriteOperationResult Execute(OperationContext operationContext, IWriteBinding binding) { using (BeginOperation()) - using (var context = RetryableWriteContext.Create(binding, _retryRequested, cancellationToken)) + using (var context = RetryableWriteContext.Create(operationContext, binding, _retryRequested)) { EnsureHintIsSupportedIfAnyRequestHasHint(); context.DisableRetriesIfAnyWriteRequestIsNotRetryable(_requests); var helper = new BatchHelper(_requests, _isOrdered, _writeConcern); foreach (var batch in helper.GetBatches()) { - batch.Result = ExecuteBatch(context, batch, cancellationToken); + batch.Result = ExecuteBatch(operationContext, context, batch); } return helper.GetFinalResultOrThrow(context.Channel.ConnectionDescription.ConnectionId); } } - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { using (BeginOperation()) - using (var context = await RetryableWriteContext.CreateAsync(binding, _retryRequested, cancellationToken).ConfigureAwait(false)) + using (var context = await RetryableWriteContext.CreateAsync(operationContext, binding, _retryRequested).ConfigureAwait(false)) { EnsureHintIsSupportedIfAnyRequestHasHint(); context.DisableRetriesIfAnyWriteRequestIsNotRetryable(_requests); var helper = new BatchHelper(_requests, _isOrdered, _writeConcern); foreach (var batch in helper.GetBatches()) { - batch.Result = await ExecuteBatchAsync(context, batch, cancellationToken).ConfigureAwait(false); + batch.Result = await ExecuteBatchAsync(operationContext, context, batch).ConfigureAwait(false); } return helper.GetFinalResultOrThrow(context.Channel.ConnectionDescription.ConnectionId); } @@ -242,14 +241,14 @@ private void EnsureHintIsSupportedIfAnyRequestHasHint() } } - private BulkWriteBatchResult ExecuteBatch(RetryableWriteContext context, Batch batch, CancellationToken cancellationToken) + private BulkWriteBatchResult ExecuteBatch(OperationContext operationContext, RetryableWriteContext context, Batch batch) { BulkWriteOperationResult result; MongoBulkWriteOperationException exception = null; try { var operation = CreateUnmixedBatchOperation(batch); - result = operation.Execute(context, cancellationToken); + result = operation.Execute(operationContext, context); } catch (MongoBulkWriteOperationException ex) { @@ -260,14 +259,14 @@ private BulkWriteBatchResult ExecuteBatch(RetryableWriteContext context, Batch b return BulkWriteBatchResult.Create(result, exception, batch.IndexMap); } - private async Task ExecuteBatchAsync(RetryableWriteContext context, Batch batch, CancellationToken cancellationToken) + private async Task ExecuteBatchAsync(OperationContext operationContext, RetryableWriteContext context, Batch batch) { BulkWriteOperationResult result; MongoBulkWriteOperationException exception = null; try { var operation = CreateUnmixedBatchOperation(batch); - result = await operation.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + result = await operation.ExecuteAsync(operationContext, context).ConfigureAwait(false); } catch (MongoBulkWriteOperationException ex) { diff --git a/src/MongoDB.Driver/Core/Operations/BulkUnmixedWriteOperationBase.cs b/src/MongoDB.Driver/Core/Operations/BulkUnmixedWriteOperationBase.cs index 616b150b155..03ad4ad3e3c 100644 --- a/src/MongoDB.Driver/Core/Operations/BulkUnmixedWriteOperationBase.cs +++ b/src/MongoDB.Driver/Core/Operations/BulkUnmixedWriteOperationBase.cs @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Driver.Core.Bindings; @@ -120,37 +119,37 @@ public WriteConcern WriteConcern } // public methods - public BulkWriteOperationResult Execute(RetryableWriteContext context, CancellationToken cancellationToken) + public BulkWriteOperationResult Execute(OperationContext operationContext, RetryableWriteContext context) { EnsureHintIsSupportedIfAnyRequestHasHint(); - return ExecuteBatches(context, cancellationToken); + return ExecuteBatches(operationContext, context); } - public BulkWriteOperationResult Execute(IWriteBinding binding, CancellationToken cancellationToken) + public BulkWriteOperationResult Execute(OperationContext operationContext, IWriteBinding binding) { using (BeginOperation()) - using (var context = RetryableWriteContext.Create(binding, _retryRequested, cancellationToken)) + using (var context = RetryableWriteContext.Create(operationContext, binding, _retryRequested)) { context.DisableRetriesIfAnyWriteRequestIsNotRetryable(_requests); - return Execute(context, cancellationToken); + return Execute(operationContext, context); } } - public Task ExecuteAsync(RetryableWriteContext context, CancellationToken cancellationToken) + public Task ExecuteAsync(OperationContext operationContext, RetryableWriteContext context) { EnsureHintIsSupportedIfAnyRequestHasHint(); - return ExecuteBatchesAsync(context, cancellationToken); + return ExecuteBatchesAsync(operationContext, context); } - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { using (BeginOperation()) - using (var context = await RetryableWriteContext.CreateAsync(binding, _retryRequested, cancellationToken).ConfigureAwait(false)) + using (var context = await RetryableWriteContext.CreateAsync(operationContext, binding, _retryRequested).ConfigureAwait(false)) { context.DisableRetriesIfAnyWriteRequestIsNotRetryable(_requests); - return await ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + return await ExecuteAsync(operationContext, context).ConfigureAwait(false); } } @@ -190,14 +189,14 @@ private void EnsureHintIsSupportedIfAnyRequestHasHint() } } - private BulkWriteBatchResult ExecuteBatch(RetryableWriteContext context, Batch batch, CancellationToken cancellationToken) + private BulkWriteBatchResult ExecuteBatch(OperationContext operationContext, RetryableWriteContext context, Batch batch) { var operation = CreateBatchOperation(batch); BsonDocument operationResult; MongoWriteConcernException writeConcernException = null; try { - operationResult = RetryableWriteOperationExecutor.Execute(operation, context, cancellationToken); + operationResult = RetryableWriteOperationExecutor.Execute(operationContext, operation, context); } catch (MongoWriteConcernException exception) when (exception.IsWriteConcernErrorOnly()) { @@ -208,14 +207,14 @@ private BulkWriteBatchResult ExecuteBatch(RetryableWriteContext context, Batch b return CreateBatchResult(batch, operationResult, writeConcernException); } - private async Task ExecuteBatchAsync(RetryableWriteContext context, Batch batch, CancellationToken cancellationToken) + private async Task ExecuteBatchAsync(OperationContext operationContext, RetryableWriteContext context, Batch batch) { var operation = CreateBatchOperation(batch); BsonDocument operationResult; MongoWriteConcernException writeConcernException = null; try { - operationResult = await RetryableWriteOperationExecutor.ExecuteAsync(operation, context, cancellationToken).ConfigureAwait(false); + operationResult = await RetryableWriteOperationExecutor.ExecuteAsync(operationContext, operation, context).ConfigureAwait(false); } catch (MongoWriteConcernException exception) when (exception.IsWriteConcernErrorOnly()) { @@ -226,22 +225,22 @@ private async Task ExecuteBatchAsync(RetryableWriteContext return CreateBatchResult(batch, operationResult, writeConcernException); } - private BulkWriteOperationResult ExecuteBatches(RetryableWriteContext context, CancellationToken cancellationToken) + private BulkWriteOperationResult ExecuteBatches(OperationContext operationContext, RetryableWriteContext context) { var helper = new BatchHelper(_requests, _writeConcern, _isOrdered); foreach (var batch in helper.GetBatches()) { - batch.Result = ExecuteBatch(context, batch, cancellationToken); + batch.Result = ExecuteBatch(operationContext, context, batch); } return helper.CreateFinalResultOrThrow(context.Channel); } - private async Task ExecuteBatchesAsync(RetryableWriteContext context, CancellationToken cancellationToken) + private async Task ExecuteBatchesAsync(OperationContext operationContext, RetryableWriteContext context) { var helper = new BatchHelper(_requests, _writeConcern, _isOrdered); foreach (var batch in helper.GetBatches()) { - batch.Result = await ExecuteBatchAsync(context, batch, cancellationToken).ConfigureAwait(false); + batch.Result = await ExecuteBatchAsync(operationContext, context, batch).ConfigureAwait(false); } return helper.CreateFinalResultOrThrow(context.Channel); } diff --git a/src/MongoDB.Driver/Core/Operations/ChangeStreamCursor.cs b/src/MongoDB.Driver/Core/Operations/ChangeStreamCursor.cs index 4dfabd9a026..afd01623847 100644 --- a/src/MongoDB.Driver/Core/Operations/ChangeStreamCursor.cs +++ b/src/MongoDB.Driver/Core/Operations/ChangeStreamCursor.cs @@ -261,13 +261,17 @@ private void ReconfigureOperationResumeValues() private IAsyncCursor Resume(CancellationToken cancellationToken) { ReconfigureOperationResumeValues(); - return _changeStreamOperation.Resume(_binding, cancellationToken); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); + return _changeStreamOperation.Resume(operationContext, _binding); } private async Task> ResumeAsync(CancellationToken cancellationToken) { ReconfigureOperationResumeValues(); - return await _changeStreamOperation.ResumeAsync(_binding, cancellationToken).ConfigureAwait(false); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); + return await _changeStreamOperation.ResumeAsync(operationContext, _binding).ConfigureAwait(false); } internal struct ResumeValues diff --git a/src/MongoDB.Driver/Core/Operations/ChangeStreamOperation.cs b/src/MongoDB.Driver/Core/Operations/ChangeStreamOperation.cs index b9ed21931b0..ed08f1c85e5 100644 --- a/src/MongoDB.Driver/Core/Operations/ChangeStreamOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/ChangeStreamOperation.cs @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization; @@ -34,8 +33,8 @@ internal interface IChangeStreamOperation : IReadOperation Resume(IReadBinding binding, CancellationToken cancellationToken); - Task> ResumeAsync(IReadBinding binding, CancellationToken cancellationToken); + IAsyncCursor Resume(OperationContext operationContext, IReadBinding binding); + Task> ResumeAsync(OperationContext operationContext, IReadBinding binding); } internal sealed class ChangeStreamOperation : IChangeStreamOperation @@ -250,7 +249,7 @@ public BsonTimestamp StartAtOperationTime // public methods /// - public IChangeStreamCursor Execute(IReadBinding binding, CancellationToken cancellationToken) + public IChangeStreamCursor Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); var bindingHandle = binding as IReadBindingHandle; @@ -262,9 +261,9 @@ public IChangeStreamCursor Execute(IReadBinding binding, CancellationTo IAsyncCursor cursor; ICursorBatchInfo cursorBatchInfo; BsonTimestamp initialOperationTime; - using (var context = RetryableReadContext.Create(binding, _retryRequested, cancellationToken)) + using (var context = RetryableReadContext.Create(operationContext, binding, _retryRequested)) { - cursor = ExecuteAggregateOperation(context, cancellationToken); + cursor = ExecuteAggregateOperation(operationContext, context); cursorBatchInfo = (ICursorBatchInfo)cursor; initialOperationTime = GetInitialOperationTimeIfRequired(context, cursorBatchInfo); @@ -285,7 +284,7 @@ public IChangeStreamCursor Execute(IReadBinding binding, CancellationTo } /// - public async Task> ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public async Task> ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); var bindingHandle = binding as IReadBindingHandle; @@ -297,9 +296,9 @@ public async Task> ExecuteAsync(IReadBinding bindin IAsyncCursor cursor; ICursorBatchInfo cursorBatchInfo; BsonTimestamp initialOperationTime; - using (var context = await RetryableReadContext.CreateAsync(binding, _retryRequested, cancellationToken).ConfigureAwait(false)) + using (var context = await RetryableReadContext.CreateAsync(operationContext, binding, _retryRequested).ConfigureAwait(false)) { - cursor = await ExecuteAggregateOperationAsync(context, cancellationToken).ConfigureAwait(false); + cursor = await ExecuteAggregateOperationAsync(operationContext, context).ConfigureAwait(false); cursorBatchInfo = (ICursorBatchInfo)cursor; initialOperationTime = GetInitialOperationTimeIfRequired(context, cursorBatchInfo); @@ -320,20 +319,20 @@ public async Task> ExecuteAsync(IReadBinding bindin } /// - public IAsyncCursor Resume(IReadBinding binding, CancellationToken cancellationToken) + public IAsyncCursor Resume(OperationContext operationContext, IReadBinding binding) { - using (var context = RetryableReadContext.Create(binding, retryRequested: false, cancellationToken)) + using (var context = RetryableReadContext.Create(operationContext, binding, retryRequested: false)) { - return ExecuteAggregateOperation(context, cancellationToken); + return ExecuteAggregateOperation(operationContext, context); } } /// - public async Task> ResumeAsync(IReadBinding binding, CancellationToken cancellationToken) + public async Task> ResumeAsync(OperationContext operationContext, IReadBinding binding) { - using (var context = await RetryableReadContext.CreateAsync(binding, retryRequested: false, cancellationToken).ConfigureAwait(false)) + using (var context = await RetryableReadContext.CreateAsync(operationContext, binding, retryRequested: false).ConfigureAwait(false)) { - return await ExecuteAggregateOperationAsync(context, cancellationToken).ConfigureAwait(false); + return await ExecuteAggregateOperationAsync(operationContext, context).ConfigureAwait(false); } } @@ -392,16 +391,16 @@ private List CreateCombinedPipeline(BsonDocument changeStreamStage return combinedPipeline; } - private IAsyncCursor ExecuteAggregateOperation(RetryableReadContext context, CancellationToken cancellationToken) + private IAsyncCursor ExecuteAggregateOperation(OperationContext operationContext, RetryableReadContext context) { var aggregateOperation = CreateAggregateOperation(); - return aggregateOperation.Execute(context, cancellationToken); + return aggregateOperation.Execute(operationContext, context); } - private Task> ExecuteAggregateOperationAsync(RetryableReadContext context, CancellationToken cancellationToken) + private Task> ExecuteAggregateOperationAsync(OperationContext operationContext, RetryableReadContext context) { var aggregateOperation = CreateAggregateOperation(); - return aggregateOperation.ExecuteAsync(context, cancellationToken); + return aggregateOperation.ExecuteAsync(operationContext, context); } private BsonDocument GetInitialPostBatchResumeTokenIfRequired(ICursorBatchInfo cursorBatchInfo) diff --git a/src/MongoDB.Driver/Core/Operations/ClientBulkWriteOperation.cs b/src/MongoDB.Driver/Core/Operations/ClientBulkWriteOperation.cs index b994a95388d..2416fd0aff3 100644 --- a/src/MongoDB.Driver/Core/Operations/ClientBulkWriteOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/ClientBulkWriteOperation.cs @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -89,17 +88,17 @@ protected override IEnumerable CreateCommandPayl return new[] { payload }; } - public new ClientBulkWriteResult Execute(IWriteBinding binding, CancellationToken cancellationToken) + public new ClientBulkWriteResult Execute(OperationContext operationContext, IWriteBinding binding) { using var operation = BeginOperation(); var bulkWriteResults = new BulkWriteRawResult(); while (true) { - using var context = RetryableWriteContext.Create(binding, GetEffectiveRetryRequested(), cancellationToken); + using var context = RetryableWriteContext.Create(operationContext, binding, GetEffectiveRetryRequested()); BsonDocument serverResponse = null; try { - serverResponse = base.Execute(context, cancellationToken); + serverResponse = base.Execute(operationContext, context); } catch (MongoWriteConcernException concernException) { @@ -124,7 +123,8 @@ protected override IEnumerable CreateCommandPayl { try { - while (individualResults.MoveNext(cancellationToken)) + // TODO: CSOT implement a way to support timeout in cursor methods + while (individualResults.MoveNext(operationContext.CancellationToken)) { PopulateIndividualResponses(individualResults.Current, bulkWriteResults); } @@ -146,17 +146,17 @@ protected override IEnumerable CreateCommandPayl } } - public new async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public new async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { using var operation = BeginOperation(); var bulkWriteResults = new BulkWriteRawResult(); while (true) { - using var context = RetryableWriteContext.Create(binding, GetEffectiveRetryRequested(), cancellationToken); + using var context = RetryableWriteContext.Create(operationContext, binding, GetEffectiveRetryRequested()); BsonDocument serverResponse = null; try { - serverResponse = await base.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + serverResponse = await base.ExecuteAsync(operationContext, context).ConfigureAwait(false); } catch (MongoWriteConcernException concernException) { @@ -181,7 +181,8 @@ protected override IEnumerable CreateCommandPayl { try { - while (await individualResults.MoveNextAsync(cancellationToken).ConfigureAwait(false)) + // TODO: CSOT implement a way to support timeout in cursor methods + while (await individualResults.MoveNextAsync(operationContext.CancellationToken).ConfigureAwait(false)) { PopulateIndividualResponses(individualResults.Current, bulkWriteResults); } diff --git a/src/MongoDB.Driver/Core/Operations/CommandOperationBase.cs b/src/MongoDB.Driver/Core/Operations/CommandOperationBase.cs index 18f18b14b6d..62eca2d6992 100644 --- a/src/MongoDB.Driver/Core/Operations/CommandOperationBase.cs +++ b/src/MongoDB.Driver/Core/Operations/CommandOperationBase.cs @@ -105,14 +105,14 @@ protected TCommandResult ExecuteProtocol(IChannelHandle channel, ICoreSessionHan } protected TCommandResult ExecuteProtocol( + OperationContext operationContext, IChannelSource channelSource, ICoreSessionHandle session, - ReadPreference readPreference, - CancellationToken cancellationToken) + ReadPreference readPreference) { - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channel = channelSource.GetChannel(operationContext)) { - return ExecuteProtocol(channel, session, readPreference, cancellationToken); + return ExecuteProtocol(channel, session, readPreference, operationContext.CancellationToken); } } @@ -136,14 +136,14 @@ protected Task ExecuteProtocolAsync(IChannelHandle channel, ICor } protected async Task ExecuteProtocolAsync( + OperationContext operationContext, IChannelSource channelSource, ICoreSessionHandle session, - ReadPreference readPreference, - CancellationToken cancellationToken) + ReadPreference readPreference) { - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) { - return await ExecuteProtocolAsync(channel, session, readPreference, cancellationToken).ConfigureAwait(false); + return await ExecuteProtocolAsync(channel, session, readPreference, operationContext.CancellationToken).ConfigureAwait(false); } } diff --git a/src/MongoDB.Driver/Core/Operations/CompositeWriteOperation.cs b/src/MongoDB.Driver/Core/Operations/CompositeWriteOperation.cs index 64a270c4486..68e6964d994 100644 --- a/src/MongoDB.Driver/Core/Operations/CompositeWriteOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/CompositeWriteOperation.cs @@ -14,7 +14,6 @@ */ using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Misc; @@ -32,12 +31,12 @@ public CompositeWriteOperation(params (IWriteOperation, bool IsMainOper Ensure.That(operations.Count(o => o.IsMainOperation) == 1, message: $"{nameof(CompositeWriteOperation)} must have a single main operation."); } - public TResult Execute(IWriteBinding binding, CancellationToken cancellationToken) + public TResult Execute(OperationContext operationContext, IWriteBinding binding) { TResult result = default; foreach (var operationInfo in _operations) { - var itemResult = operationInfo.Operation.Execute(binding, cancellationToken); + var itemResult = operationInfo.Operation.Execute(operationContext, binding); if (operationInfo.IsMainOperation) { result = itemResult; @@ -47,12 +46,12 @@ public TResult Execute(IWriteBinding binding, CancellationToken cancellationToke return result; } - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { TResult result = default; foreach (var operationInfo in _operations) { - var itemResult = await operationInfo.Operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); + var itemResult = await operationInfo.Operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); if (operationInfo.IsMainOperation) { result = itemResult; diff --git a/src/MongoDB.Driver/Core/Operations/CountDocumentsOperation.cs b/src/MongoDB.Driver/Core/Operations/CountDocumentsOperation.cs index 2871029e2a1..6bcc00d33db 100644 --- a/src/MongoDB.Driver/Core/Operations/CountDocumentsOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/CountDocumentsOperation.cs @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -110,28 +109,28 @@ public long? Skip set { _skip = value; } } - public long Execute(IReadBinding binding, CancellationToken cancellationToken) + public long Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) { var operation = CreateOperation(); - var cursor = operation.Execute(binding, cancellationToken); - var result = cursor.ToList(cancellationToken); + var cursor = operation.Execute(operationContext, binding); + var result = cursor.ToList(operationContext.CancellationToken); return ExtractCountFromResult(result); } } - public async Task ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) { var operation = CreateOperation(); - var cursor = await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); - var result = await cursor.ToListAsync(cancellationToken).ConfigureAwait(false); + var cursor = await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); + var result = await cursor.ToListAsync(operationContext.CancellationToken).ConfigureAwait(false); return ExtractCountFromResult(result); } } diff --git a/src/MongoDB.Driver/Core/Operations/CountOperation.cs b/src/MongoDB.Driver/Core/Operations/CountOperation.cs index dc33440c61f..cd83e1d374d 100644 --- a/src/MongoDB.Driver/Core/Operations/CountOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/CountOperation.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -127,39 +126,39 @@ public BsonDocument CreateCommand(ConnectionDescription connectionDescription, I }; } - public long Execute(IReadBinding binding, CancellationToken cancellationToken) + public long Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var context = RetryableReadContext.Create(binding, _retryRequested, cancellationToken)) + using (var context = RetryableReadContext.Create(operationContext, binding, _retryRequested)) { - return Execute(context, cancellationToken); + return Execute(operationContext, context); } } - public long Execute(RetryableReadContext context, CancellationToken cancellationToken) + public long Execute(OperationContext operationContext, RetryableReadContext context) { var operation = CreateOperation(context); - var document = operation.Execute(context, cancellationToken); + var document = operation.Execute(operationContext, context); return document["n"].ToInt64(); } - public async Task ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var context = await RetryableReadContext.CreateAsync(binding, _retryRequested, cancellationToken).ConfigureAwait(false)) + using (var context = await RetryableReadContext.CreateAsync(operationContext, binding, _retryRequested).ConfigureAwait(false)) { - return await ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + return await ExecuteAsync(operationContext, context).ConfigureAwait(false); } } - public async Task ExecuteAsync(RetryableReadContext context, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, RetryableReadContext context) { var operation = CreateOperation(context); - var document = await operation.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + var document = await operation.ExecuteAsync(operationContext, context).ConfigureAwait(false); return document["n"].ToInt64(); } diff --git a/src/MongoDB.Driver/Core/Operations/CreateCollectionOperation.cs b/src/MongoDB.Driver/Core/Operations/CreateCollectionOperation.cs index 9f9923a5c75..31b2f7be994 100644 --- a/src/MongoDB.Driver/Core/Operations/CreateCollectionOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/CreateCollectionOperation.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -274,36 +273,36 @@ internal BsonDocument CreateCommand(ICoreSessionHandle session) } } - public BsonDocument Execute(IWriteBinding binding, CancellationToken cancellationToken) + public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var channelSource = binding.GetWriteChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetWriteChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) { EnsureServerIsValid(channel.ConnectionDescription.MaxWireVersion); using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session); - return operation.Execute(channelBinding, cancellationToken); + return operation.Execute(operationContext, channelBinding); } } } - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var channelSource = await binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetWriteChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) { EnsureServerIsValid(channel.ConnectionDescription.MaxWireVersion); using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session); - return await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); } } } diff --git a/src/MongoDB.Driver/Core/Operations/CreateIndexesOperation.cs b/src/MongoDB.Driver/Core/Operations/CreateIndexesOperation.cs index 1a19dc5787a..f4e071950ad 100644 --- a/src/MongoDB.Driver/Core/Operations/CreateIndexesOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/CreateIndexesOperation.cs @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -87,27 +86,27 @@ public TimeSpan? MaxTime set { _maxTime = Ensure.IsNullOrInfiniteOrGreaterThanOrEqualToZero(value, nameof(value)); } } - public BsonDocument Execute(IWriteBinding binding, CancellationToken cancellationToken) + public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) { using (BeginOperation()) - using (var channelSource = binding.GetWriteChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetWriteChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session, channel.ConnectionDescription); - return operation.Execute(channelBinding, cancellationToken); + return operation.Execute(operationContext, channelBinding); } } - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { using (BeginOperation()) - using (var channelSource = await binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetWriteChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session, channel.ConnectionDescription); - return await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); } } diff --git a/src/MongoDB.Driver/Core/Operations/CreateSearchIndexesOperation.cs b/src/MongoDB.Driver/Core/Operations/CreateSearchIndexesOperation.cs index bb96493a076..edc8ae04f77 100644 --- a/src/MongoDB.Driver/Core/Operations/CreateSearchIndexesOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/CreateSearchIndexesOperation.cs @@ -15,7 +15,6 @@ using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -55,28 +54,28 @@ public CreateSearchIndexesOperation( // public methods /// - public BsonDocument Execute(IWriteBinding binding, CancellationToken cancellationToken) + public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) { using (EventContext.BeginOperation("createSearchIndexes")) - using (var channelSource = binding.GetWriteChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetWriteChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(); - return operation.Execute(channelBinding, cancellationToken); + return operation.Execute(operationContext, channelBinding); } } /// - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { using (EventContext.BeginOperation("createSearchIndexes")) - using (var channelSource = await binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetWriteChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(); - return await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); } } diff --git a/src/MongoDB.Driver/Core/Operations/CreateViewOperation.cs b/src/MongoDB.Driver/Core/Operations/CreateViewOperation.cs index cd291bfbf19..64a5d954bc2 100644 --- a/src/MongoDB.Driver/Core/Operations/CreateViewOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/CreateViewOperation.cs @@ -15,7 +15,6 @@ using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -87,29 +86,29 @@ public WriteConcern WriteConcern set { _writeConcern = value; } } - public BsonDocument Execute(IWriteBinding binding, CancellationToken cancellationToken) + public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); - using (var channelSource = binding.GetWriteChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetWriteChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session, channel.ConnectionDescription); - return operation.Execute(channelBinding, cancellationToken); + return operation.Execute(operationContext, channelBinding); } } - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); - using (var channelSource = await binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetWriteChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session, channel.ConnectionDescription); - return await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); } } diff --git a/src/MongoDB.Driver/Core/Operations/DatabaseExistsOperation.cs b/src/MongoDB.Driver/Core/Operations/DatabaseExistsOperation.cs index ec4b4a42e4a..9b6f4cdca4d 100644 --- a/src/MongoDB.Driver/Core/Operations/DatabaseExistsOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/DatabaseExistsOperation.cs @@ -14,7 +14,6 @@ */ using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Misc; @@ -50,21 +49,23 @@ public bool RetryRequested set { _retryRequested = value; } } - public bool Execute(IReadBinding binding, CancellationToken cancellationToken) + public bool Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); var operation = CreateOperation(); - var result = operation.Execute(binding, cancellationToken); - var list = result.ToList(cancellationToken); + var result = operation.Execute(operationContext, binding); + // TODO: CSOT find a way to apply CSOT timeout to ToList as well. + var list = result.ToList(operationContext.CancellationToken); return list.Any(x => x["name"] == _databaseNamespace.DatabaseName); } - public async Task ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); var operation = CreateOperation(); - var result = await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); - var list = await result.ToListAsync(cancellationToken).ConfigureAwait(false); + var result = await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); + // TODO: CSOT find a way to apply CSOT timeout to ToList as well. + var list = await result.ToListAsync(operationContext.CancellationToken).ConfigureAwait(false); return list.Any(x => x["name"] == _databaseNamespace.DatabaseName); } diff --git a/src/MongoDB.Driver/Core/Operations/DistinctOperation.cs b/src/MongoDB.Driver/Core/Operations/DistinctOperation.cs index 441996f2889..0e25eb3d097 100644 --- a/src/MongoDB.Driver/Core/Operations/DistinctOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/DistinctOperation.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.IO; @@ -105,15 +104,15 @@ public IBsonSerializer ValueSerializer get { return _valueSerializer; } } - public IAsyncCursor Execute(IReadBinding binding, CancellationToken cancellationToken) + public IAsyncCursor Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var context = RetryableReadContext.Create(binding, _retryRequested, cancellationToken)) + using (var context = RetryableReadContext.Create(operationContext, binding, _retryRequested)) { var operation = CreateOperation(context); - var result = operation.Execute(context, cancellationToken); + var result = operation.Execute(operationContext, context); binding.Session.SetSnapshotTimeIfNeeded(result.AtClusterTime); @@ -121,15 +120,15 @@ public IAsyncCursor Execute(IReadBinding binding, CancellationToken canc } } - public async Task> ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public async Task> ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var context = await RetryableReadContext.CreateAsync(binding, _retryRequested, cancellationToken).ConfigureAwait(false)) + using (var context = await RetryableReadContext.CreateAsync(operationContext, binding, _retryRequested).ConfigureAwait(false)) { var operation = CreateOperation(context); - var result = await operation.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + var result = await operation.ExecuteAsync(operationContext, context).ConfigureAwait(false); binding.Session.SetSnapshotTimeIfNeeded(result.AtClusterTime); diff --git a/src/MongoDB.Driver/Core/Operations/DropCollectionOperation.cs b/src/MongoDB.Driver/Core/Operations/DropCollectionOperation.cs index fa7e11f8c70..d667367575a 100644 --- a/src/MongoDB.Driver/Core/Operations/DropCollectionOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/DropCollectionOperation.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -96,20 +95,20 @@ public WriteConcern WriteConcern set { _writeConcern = value; } } - public BsonDocument Execute(IWriteBinding binding, CancellationToken cancellationToken) + public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var channelSource = binding.GetWriteChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetWriteChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session); BsonDocument result; try { - result = operation.Execute(channelBinding, cancellationToken); + result = operation.Execute(operationContext, channelBinding); } catch (MongoCommandException ex) { @@ -123,20 +122,20 @@ public BsonDocument Execute(IWriteBinding binding, CancellationToken cancellatio } } - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var channelSource = await binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetWriteChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session); BsonDocument result; try { - result = await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false); + result = await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); } catch (MongoCommandException ex) { diff --git a/src/MongoDB.Driver/Core/Operations/DropDatabaseOperation.cs b/src/MongoDB.Driver/Core/Operations/DropDatabaseOperation.cs index 315a5307409..39be674d49c 100644 --- a/src/MongoDB.Driver/Core/Operations/DropDatabaseOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/DropDatabaseOperation.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -53,8 +52,8 @@ public WriteConcern WriteConcern { get { return _writeConcern; } set { _writeConcern = value; } - } - + } + public BsonDocument CreateCommand(ICoreSessionHandle session) { var writeConcern = WriteConcernHelper.GetEffectiveWriteConcern(session, _writeConcern); @@ -65,31 +64,31 @@ public BsonDocument CreateCommand(ICoreSessionHandle session) }; } - public BsonDocument Execute(IWriteBinding binding, CancellationToken cancellationToken) + public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var channelSource = binding.GetWriteChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetWriteChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session); - return operation.Execute(channelBinding, cancellationToken); + return operation.Execute(operationContext, channelBinding); } } - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var channelSource = await binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetWriteChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session); - return await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); } } diff --git a/src/MongoDB.Driver/Core/Operations/DropIndexOperation.cs b/src/MongoDB.Driver/Core/Operations/DropIndexOperation.cs index 577ed62e448..ef68ad071c5 100644 --- a/src/MongoDB.Driver/Core/Operations/DropIndexOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/DropIndexOperation.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -98,20 +97,20 @@ public BsonDocument CreateCommand(ICoreSessionHandle session) }; } - public BsonDocument Execute(IWriteBinding binding, CancellationToken cancellationToken) + public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var channelSource = binding.GetWriteChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetWriteChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session); BsonDocument result; try { - result = operation.Execute(channelBinding, cancellationToken); + result = operation.Execute(operationContext, channelBinding); } catch (MongoCommandException ex) { @@ -125,20 +124,20 @@ public BsonDocument Execute(IWriteBinding binding, CancellationToken cancellatio } } - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var channelSource = await binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetWriteChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session); BsonDocument result; try { - result = await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false); + result = await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); } catch (MongoCommandException ex) { @@ -150,7 +149,7 @@ public async Task ExecuteAsync(IWriteBinding binding, Cancellation } return result; } - } + } private IDisposable BeginOperation() => EventContext.BeginOperation("dropIndexes"); diff --git a/src/MongoDB.Driver/Core/Operations/DropSearchIndexOperation.cs b/src/MongoDB.Driver/Core/Operations/DropSearchIndexOperation.cs index 653039feb98..aff890be381 100644 --- a/src/MongoDB.Driver/Core/Operations/DropSearchIndexOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/DropSearchIndexOperation.cs @@ -13,7 +13,6 @@ * limitations under the License. */ -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -63,20 +62,20 @@ private WriteCommandOperation CreateOperation() => new(_collectionNamespace.DatabaseNamespace, CreateCommand(), BsonDocumentSerializer.Instance, _messageEncoderSettings); /// - public BsonDocument Execute(IWriteBinding binding, CancellationToken cancellationToken) + public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (EventContext.BeginOperation("dropSearchIndex")) - using (var channelSource = binding.GetWriteChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetWriteChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(); try { - return operation.Execute(channelBinding, cancellationToken); + return operation.Execute(operationContext, channelBinding); } catch (MongoCommandException ex) when (ShouldIgnoreException(ex)) { @@ -86,20 +85,20 @@ public BsonDocument Execute(IWriteBinding binding, CancellationToken cancellatio } /// - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (EventContext.BeginOperation("dropSearchIndex")) - using (var channelSource = await binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetWriteChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(); try { - return await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); } catch (MongoCommandException ex) when (ShouldIgnoreException(ex)) { diff --git a/src/MongoDB.Driver/Core/Operations/EndTransactionOperation.cs b/src/MongoDB.Driver/Core/Operations/EndTransactionOperation.cs index 90e5f44c882..0544b2498c3 100644 --- a/src/MongoDB.Driver/Core/Operations/EndTransactionOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/EndTransactionOperation.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -51,29 +50,29 @@ public MessageEncoderSettings MessageEncoderSettings protected abstract string CommandName { get; } - public virtual BsonDocument Execute(IReadBinding binding, CancellationToken cancellationToken) + public virtual BsonDocument Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); - using (var channelSource = binding.GetReadChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetReadChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(); - return operation.Execute(channelBinding, cancellationToken); + return operation.Execute(operationContext, channelBinding); } } - public virtual async Task ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public virtual async Task ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); - using (var channelSource = await binding.GetReadChannelSourceAsync(cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetReadChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(); - return await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); } } @@ -134,11 +133,11 @@ public TimeSpan? MaxCommitTime protected override string CommandName => "commitTransaction"; - public override BsonDocument Execute(IReadBinding binding, CancellationToken cancellationToken) + public override BsonDocument Execute(OperationContext operationContext, IReadBinding binding) { try { - return base.Execute(binding, cancellationToken); + return base.Execute(operationContext, binding); } catch (MongoException exception) when (ShouldReplaceTransientTransactionErrorWithUnknownTransactionCommitResult(exception)) { @@ -147,11 +146,11 @@ public override BsonDocument Execute(IReadBinding binding, CancellationToken can } } - public override async Task ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public override async Task ExecuteAsync(OperationContext operationContext, IReadBinding binding) { try { - return await base.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); + return await base.ExecuteAsync(operationContext, binding).ConfigureAwait(false); } catch (MongoException exception) when (ShouldReplaceTransientTransactionErrorWithUnknownTransactionCommitResult(exception)) { diff --git a/src/MongoDB.Driver/Core/Operations/EstimatedDocumentCountOperation.cs b/src/MongoDB.Driver/Core/Operations/EstimatedDocumentCountOperation.cs index 27958d906f4..eac2d1a32a1 100644 --- a/src/MongoDB.Driver/Core/Operations/EstimatedDocumentCountOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/EstimatedDocumentCountOperation.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Driver.Core.Bindings; @@ -67,29 +66,29 @@ public bool RetryRequested set => _retryRequested = value; } - public long Execute(IReadBinding binding, CancellationToken cancellationToken) + public long Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var context = RetryableReadContext.Create(binding, _retryRequested, cancellationToken)) + using (var context = RetryableReadContext.Create(operationContext, binding, _retryRequested)) { var operation = CreateCountOperation(); - return operation.Execute(context, cancellationToken); + return operation.Execute(operationContext, context); } } - public async Task ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var context = RetryableReadContext.Create(binding, _retryRequested, cancellationToken)) + using (var context = RetryableReadContext.Create(operationContext, binding, _retryRequested)) { var operation = CreateCountOperation(); - return await operation.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, context).ConfigureAwait(false); } } diff --git a/src/MongoDB.Driver/Core/Operations/EvalOperation.cs b/src/MongoDB.Driver/Core/Operations/EvalOperation.cs index 218d4b91e11..b8bb23cfce5 100644 --- a/src/MongoDB.Driver/Core/Operations/EvalOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/EvalOperation.cs @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -75,8 +74,8 @@ public bool? NoLock { get { return _noLock; } set { _noLock = value; } - } - + } + public BsonDocument CreateCommand() { return new BsonDocument @@ -88,19 +87,19 @@ public BsonDocument CreateCommand() }; } - public BsonValue Execute(IWriteBinding binding, CancellationToken cancellationToken) + public BsonValue Execute(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); var operation = CreateOperation(); - var result = operation.Execute(binding, cancellationToken); + var result = operation.Execute(operationContext, binding); return result["retval"]; } - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); var operation = CreateOperation(); - var result = await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); + var result = await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); return result["retval"]; } diff --git a/src/MongoDB.Driver/Core/Operations/FindAndModifyOperationBase.cs b/src/MongoDB.Driver/Core/Operations/FindAndModifyOperationBase.cs index 8bad1138f40..d32198f81af 100644 --- a/src/MongoDB.Driver/Core/Operations/FindAndModifyOperationBase.cs +++ b/src/MongoDB.Driver/Core/Operations/FindAndModifyOperationBase.cs @@ -15,7 +15,6 @@ using System; using System.Text; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.IO; @@ -85,39 +84,39 @@ public bool RetryRequested set { _retryRequested = value; } } - public TResult Execute(IWriteBinding binding, CancellationToken cancellationToken) + public TResult Execute(OperationContext operationContext, IWriteBinding binding) { using (BeginOperation()) { - return RetryableWriteOperationExecutor.Execute(this, binding, _retryRequested, cancellationToken); + return RetryableWriteOperationExecutor.Execute(operationContext, this, binding, _retryRequested); } } - public TResult Execute(RetryableWriteContext context, CancellationToken cancellationToken) + public TResult Execute(OperationContext operationContext, RetryableWriteContext context) { using (BeginOperation()) { - return RetryableWriteOperationExecutor.Execute(this, context, cancellationToken); + return RetryableWriteOperationExecutor.Execute(operationContext, this, context); } } - public Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { using (BeginOperation()) - { - return RetryableWriteOperationExecutor.ExecuteAsync(this, binding, _retryRequested, cancellationToken); + { + return RetryableWriteOperationExecutor.ExecuteAsync(operationContext, this, binding, _retryRequested); } } - public Task ExecuteAsync(RetryableWriteContext context, CancellationToken cancellationToken) + public Task ExecuteAsync(OperationContext operationContext, RetryableWriteContext context) { using (BeginOperation()) { - return RetryableWriteOperationExecutor.ExecuteAsync(this, context, cancellationToken); + return RetryableWriteOperationExecutor.ExecuteAsync(operationContext, this, context); } } - public TResult ExecuteAttempt(RetryableWriteContext context, int attempt, long? transactionNumber, CancellationToken cancellationToken) + public TResult ExecuteAttempt(OperationContext operationContext, RetryableWriteContext context, int attempt, long? transactionNumber) { var binding = context.Binding; var channelSource = context.ChannelSource; @@ -126,14 +125,14 @@ public TResult ExecuteAttempt(RetryableWriteContext context, int attempt, long? using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session, channel.ConnectionDescription, transactionNumber); - using (var rawBsonDocument = operation.Execute(channelBinding, cancellationToken)) + using (var rawBsonDocument = operation.Execute(operationContext, channelBinding)) { return ProcessCommandResult(channel.ConnectionDescription.ConnectionId, rawBsonDocument); } } } - public async Task ExecuteAttemptAsync(RetryableWriteContext context, int attempt, long? transactionNumber, CancellationToken cancellationToken) + public async Task ExecuteAttemptAsync(OperationContext operationContext, RetryableWriteContext context, int attempt, long? transactionNumber) { var binding = context.Binding; var channelSource = context.ChannelSource; @@ -142,17 +141,17 @@ public async Task ExecuteAttemptAsync(RetryableWriteContext context, in using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session, channel.ConnectionDescription, transactionNumber); - using (var rawBsonDocument = await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false)) + using (var rawBsonDocument = await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false)) { return ProcessCommandResult(channel.ConnectionDescription.ConnectionId, rawBsonDocument); } } - } - + } + public abstract BsonDocument CreateCommand(ICoreSessionHandle session, ConnectionDescription connectionDescription, long? transactionNumber); - protected abstract IElementNameValidator GetCommandValidator(); - + protected abstract IElementNameValidator GetCommandValidator(); + private IDisposable BeginOperation() => EventContext.BeginOperation("findAndModify"); private WriteCommandOperation CreateOperation(ICoreSessionHandle session, ConnectionDescription connectionDescription, long? transactionNumber) diff --git a/src/MongoDB.Driver/Core/Operations/FindOperation.cs b/src/MongoDB.Driver/Core/Operations/FindOperation.cs index 236476cf4c2..7ebc524b81e 100644 --- a/src/MongoDB.Driver/Core/Operations/FindOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/FindOperation.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization; @@ -292,48 +291,48 @@ public BsonDocument CreateCommand(ConnectionDescription connectionDescription, I }; } - public IAsyncCursor Execute(IReadBinding binding, CancellationToken cancellationToken = default(CancellationToken)) + public IAsyncCursor Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var context = RetryableReadContext.Create(binding, _retryRequested, cancellationToken)) + using (var context = RetryableReadContext.Create(operationContext, binding, _retryRequested)) { - return Execute(context, cancellationToken); + return Execute(operationContext, context); } } - public IAsyncCursor Execute(RetryableReadContext context, CancellationToken cancellationToken = default(CancellationToken)) + public IAsyncCursor Execute(OperationContext operationContext, RetryableReadContext context) { Ensure.IsNotNull(context, nameof(context)); using (EventContext.BeginFind(_batchSize, _limit)) { var operation = CreateOperation(context); - var commandResult = operation.Execute(context, cancellationToken); + var commandResult = operation.Execute(operationContext, context); return CreateCursor(context.ChannelSource, context.Channel, commandResult); } } - public async Task> ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken = default(CancellationToken)) + public async Task> ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var context = await RetryableReadContext.CreateAsync(binding, _retryRequested, cancellationToken).ConfigureAwait(false)) + using (var context = await RetryableReadContext.CreateAsync(operationContext, binding, _retryRequested).ConfigureAwait(false)) { - return await ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + return await ExecuteAsync(operationContext, context).ConfigureAwait(false); } } - public async Task> ExecuteAsync(RetryableReadContext context, CancellationToken cancellationToken = default(CancellationToken)) + public async Task> ExecuteAsync(OperationContext operationContext, RetryableReadContext context) { Ensure.IsNotNull(context, nameof(context)); using (EventContext.BeginFind(_batchSize, _limit)) { var operation = CreateOperation(context); - var commandResult = await operation.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + var commandResult = await operation.ExecuteAsync(operationContext, context).ConfigureAwait(false); return CreateCursor(context.ChannelSource, context.Channel, commandResult); } } diff --git a/src/MongoDB.Driver/Core/Operations/GroupOperation.cs b/src/MongoDB.Driver/Core/Operations/GroupOperation.cs index 7ae72c2444b..4c987e09300 100644 --- a/src/MongoDB.Driver/Core/Operations/GroupOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/GroupOperation.cs @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization; @@ -139,27 +138,27 @@ public BsonDocument CreateCommand() }; } - public IEnumerable Execute(IReadBinding binding, CancellationToken cancellationToken) + public IEnumerable Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); - using (var channelSource = binding.GetReadChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetReadChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) using (var channelBinding = new ChannelReadBinding(channelSource.Server, channel, binding.ReadPreference, binding.Session.Fork())) { var operation = CreateOperation(); - return operation.Execute(channelBinding, cancellationToken); + return operation.Execute(operationContext, channelBinding); } } - public async Task> ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public async Task> ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); - using (var channelSource = await binding.GetReadChannelSourceAsync(cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetReadChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) using (var channelBinding = new ChannelReadBinding(channelSource.Server, channel, binding.ReadPreference, binding.Session.Fork())) { var operation = CreateOperation(); - return await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); } } diff --git a/src/MongoDB.Driver/Core/Operations/IOperation.cs b/src/MongoDB.Driver/Core/Operations/IOperation.cs index 06fa1fbabbf..1ccd8dcb2ad 100644 --- a/src/MongoDB.Driver/Core/Operations/IOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/IOperation.cs @@ -13,7 +13,6 @@ * limitations under the License. */ -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Bindings; @@ -21,13 +20,13 @@ namespace MongoDB.Driver.Core.Operations { internal interface IReadOperation { - TResult Execute(IReadBinding binding, CancellationToken cancellationToken); - Task ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken); + TResult Execute(OperationContext operationContext, IReadBinding binding); + Task ExecuteAsync(OperationContext operationContext, IReadBinding binding); } internal interface IWriteOperation { - TResult Execute(IWriteBinding binding, CancellationToken cancellationToken); - Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken); + TResult Execute(OperationContext operationContext, IWriteBinding binding); + Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding); } } diff --git a/src/MongoDB.Driver/Core/Operations/IRetryableOperation.cs b/src/MongoDB.Driver/Core/Operations/IRetryableOperation.cs index efa510c1553..d29a8948477 100644 --- a/src/MongoDB.Driver/Core/Operations/IRetryableOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/IRetryableOperation.cs @@ -13,34 +13,33 @@ * limitations under the License. */ -using System.Threading; using System.Threading.Tasks; namespace MongoDB.Driver.Core.Operations { internal interface IExecutableInRetryableReadContext { - TResult Execute(RetryableReadContext context, CancellationToken cancellationToken); - Task ExecuteAsync(RetryableReadContext context, CancellationToken cancellationToken); + TResult Execute(OperationContext operationContext, RetryableReadContext context); + Task ExecuteAsync(OperationContext operationContext, RetryableReadContext context); } internal interface IExecutableInRetryableWriteContext { - TResult Execute(RetryableWriteContext context, CancellationToken cancellationToken); - Task ExecuteAsync(RetryableWriteContext context, CancellationToken cancellationToken); + TResult Execute(OperationContext operationContext, RetryableWriteContext context); + Task ExecuteAsync(OperationContext operationContext, RetryableWriteContext context); } internal interface IRetryableReadOperation : IExecutableInRetryableReadContext { - TResult ExecuteAttempt(RetryableReadContext context, int attempt, long? transactionNumber, CancellationToken cancellationToken); - Task ExecuteAttemptAsync(RetryableReadContext context, int attempt, long? transactionNumber, CancellationToken cancellationToken); + TResult ExecuteAttempt(OperationContext operationContext, RetryableReadContext context, int attempt, long? transactionNumber); + Task ExecuteAttemptAsync(OperationContext operationContext, RetryableReadContext context, int attempt, long? transactionNumber); } internal interface IRetryableWriteOperation : IExecutableInRetryableWriteContext { WriteConcern WriteConcern { get; } - TResult ExecuteAttempt(RetryableWriteContext context, int attempt, long? transactionNumber, CancellationToken cancellationToken); - Task ExecuteAttemptAsync(RetryableWriteContext context, int attempt, long? transactionNumber, CancellationToken cancellationToken); + TResult ExecuteAttempt(OperationContext operationContext, RetryableWriteContext context, int attempt, long? transactionNumber); + Task ExecuteAttemptAsync(OperationContext operationContext, RetryableWriteContext context, int attempt, long? transactionNumber); } } diff --git a/src/MongoDB.Driver/Core/Operations/ListCollectionsOperation.cs b/src/MongoDB.Driver/Core/Operations/ListCollectionsOperation.cs index e25b97bbbd7..320c1c3ea71 100644 --- a/src/MongoDB.Driver/Core/Operations/ListCollectionsOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/ListCollectionsOperation.cs @@ -15,7 +15,6 @@ using System; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -91,52 +90,52 @@ public bool RetryRequested set => _retryRequested = value; } - public IAsyncCursor Execute(IReadBinding binding, CancellationToken cancellationToken) + public IAsyncCursor Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) { - using (var context = RetryableReadContext.Create(binding, _retryRequested, cancellationToken)) + using (var context = RetryableReadContext.Create(operationContext, binding, _retryRequested)) { - return Execute(context, cancellationToken); + return Execute(operationContext, context); } } } - public IAsyncCursor Execute(RetryableReadContext context, CancellationToken cancellationToken) + public IAsyncCursor Execute(OperationContext operationContext, RetryableReadContext context) { Ensure.IsNotNull(context, nameof(context)); using (BeginOperation()) { var operation = CreateOperation(); - var result = operation.Execute(context, cancellationToken); + var result = operation.Execute(operationContext, context); return CreateCursor(context.ChannelSource, context.Channel, result); } } - public async Task> ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public async Task> ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) { - using (var context = await RetryableReadContext.CreateAsync(binding, _retryRequested, cancellationToken).ConfigureAwait(false)) + using (var context = await RetryableReadContext.CreateAsync(operationContext, binding, _retryRequested).ConfigureAwait(false)) { - return await ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + return await ExecuteAsync(operationContext, context).ConfigureAwait(false); } } } - public async Task> ExecuteAsync(RetryableReadContext context, CancellationToken cancellationToken) + public async Task> ExecuteAsync(OperationContext operationContext, RetryableReadContext context) { Ensure.IsNotNull(context, nameof(context)); using (BeginOperation()) { var operation = CreateOperation(); - var result = await operation.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + var result = await operation.ExecuteAsync(operationContext, context).ConfigureAwait(false); return CreateCursor(context.ChannelSource, context.Channel, result); } } diff --git a/src/MongoDB.Driver/Core/Operations/ListDatabasesOperation.cs b/src/MongoDB.Driver/Core/Operations/ListDatabasesOperation.cs index fb9ec4fbfa5..e55151e5b5c 100644 --- a/src/MongoDB.Driver/Core/Operations/ListDatabasesOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/ListDatabasesOperation.cs @@ -15,7 +15,6 @@ using System; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -73,8 +72,8 @@ public bool RetryRequested { get { return _retryRequested; } set { _retryRequested = value; } - } - + } + public BsonDocument CreateCommand() { return new BsonDocument @@ -87,26 +86,26 @@ public BsonDocument CreateCommand() }; } - public IAsyncCursor Execute(IReadBinding binding, CancellationToken cancellationToken) + public IAsyncCursor Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) { var operation = CreateOperation(); - var reply = operation.Execute(binding, cancellationToken); + var reply = operation.Execute(operationContext, binding); return CreateCursor(reply); } } - public async Task> ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public async Task> ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) { var operation = CreateOperation(); - var reply = await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); + var reply = await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); return CreateCursor(reply); } } diff --git a/src/MongoDB.Driver/Core/Operations/ListIndexesOperation.cs b/src/MongoDB.Driver/Core/Operations/ListIndexesOperation.cs index 1bd4fb40102..b91633c4716 100644 --- a/src/MongoDB.Driver/Core/Operations/ListIndexesOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/ListIndexesOperation.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Driver.Core.Bindings; @@ -68,27 +67,27 @@ public bool RetryRequested set => _retryRequested = value; } - public IAsyncCursor Execute(IReadBinding binding, CancellationToken cancellationToken) + public IAsyncCursor Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var context = RetryableReadContext.Create(binding, _retryRequested, cancellationToken)) + using (var context = RetryableReadContext.Create(operationContext, binding, _retryRequested)) { var operation = CreateOperation(); - return operation.Execute(context, cancellationToken); + return operation.Execute(operationContext, context); } } - public async Task> ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public async Task> ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var context = await RetryableReadContext.CreateAsync(binding, _retryRequested, cancellationToken).ConfigureAwait(false)) + using (var context = await RetryableReadContext.CreateAsync(operationContext, binding, _retryRequested).ConfigureAwait(false)) { var operation = CreateOperation(); - return await operation.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, context).ConfigureAwait(false); } } diff --git a/src/MongoDB.Driver/Core/Operations/ListIndexesUsingCommandOperation.cs b/src/MongoDB.Driver/Core/Operations/ListIndexesUsingCommandOperation.cs index a16ba814dbc..290bbbf00e5 100644 --- a/src/MongoDB.Driver/Core/Operations/ListIndexesUsingCommandOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/ListIndexesUsingCommandOperation.cs @@ -15,7 +15,6 @@ using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -70,17 +69,17 @@ public bool RetryRequested set => _retryRequested = value; } - public IAsyncCursor Execute(IReadBinding binding, CancellationToken cancellationToken) + public IAsyncCursor Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); - using (var context = RetryableReadContext.Create(binding, _retryRequested, cancellationToken)) + using (var context = RetryableReadContext.Create(operationContext, binding, _retryRequested)) { - return Execute(context, cancellationToken); + return Execute(operationContext, context); } } - public IAsyncCursor Execute(RetryableReadContext context, CancellationToken cancellationToken) + public IAsyncCursor Execute(OperationContext operationContext, RetryableReadContext context) { Ensure.IsNotNull(context, nameof(context)); @@ -89,7 +88,7 @@ public IAsyncCursor Execute(RetryableReadContext context, Cancella var operation = CreateOperation(); try { - var result = operation.Execute(context, cancellationToken); + var result = operation.Execute(operationContext, context); return CreateCursor(context.ChannelSource, context.Channel, result); } catch (MongoCommandException ex) when (IsCollectionNotFoundException(ex)) @@ -99,17 +98,17 @@ public IAsyncCursor Execute(RetryableReadContext context, Cancella } } - public async Task> ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public async Task> ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); - using (var context = await RetryableReadContext.CreateAsync(binding, _retryRequested, cancellationToken).ConfigureAwait(false)) + using (var context = await RetryableReadContext.CreateAsync(operationContext, binding, _retryRequested).ConfigureAwait(false)) { - return await ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + return await ExecuteAsync(operationContext, context).ConfigureAwait(false); } } - public async Task> ExecuteAsync(RetryableReadContext context, CancellationToken cancellationToken) + public async Task> ExecuteAsync(OperationContext operationContext, RetryableReadContext context) { Ensure.IsNotNull(context, nameof(context)); @@ -118,7 +117,7 @@ public async Task> ExecuteAsync(RetryableReadContext var operation = CreateOperation(); try { - var result = await operation.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + var result = await operation.ExecuteAsync(operationContext, context).ConfigureAwait(false); return CreateCursor(context.ChannelSource, context.Channel, result); } catch (MongoCommandException ex) when (IsCollectionNotFoundException(ex)) diff --git a/src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs b/src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs index a509cfd33ed..a68b2227278 100644 --- a/src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization; @@ -88,31 +87,31 @@ protected override BsonDocument CreateOutputOptions() } /// - public IAsyncCursor Execute(IReadBinding binding, CancellationToken cancellationToken) + public IAsyncCursor Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); - using (var channelSource = binding.GetReadChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetReadChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) using (var channelBinding = new ChannelReadBinding(channelSource.Server, channel, binding.ReadPreference, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session, channel.ConnectionDescription); - var result = operation.Execute(channelBinding, cancellationToken); + var result = operation.Execute(operationContext, channelBinding); return new SingleBatchAsyncCursor(result); } } /// - public async Task> ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public async Task> ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); - using (var channelSource = await binding.GetReadChannelSourceAsync(cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetReadChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) using (var channelBinding = new ChannelReadBinding(channelSource.Server, channel, binding.ReadPreference, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session, channel.ConnectionDescription); - var result = await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false); + var result = await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); return new SingleBatchAsyncCursor(result); } } diff --git a/src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs b/src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs index 53e787daf15..99e32b1adcd 100644 --- a/src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -170,30 +169,30 @@ protected override BsonDocument CreateOutputOptions() } /// - public BsonDocument Execute(IWriteBinding binding, CancellationToken cancellationToken) + public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); - using (var channelSource = binding.GetWriteChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetWriteChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session, channel.ConnectionDescription); - return operation.Execute(channelBinding, cancellationToken); + return operation.Execute(operationContext, channelBinding); } } /// - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); - using (var channelSource = await binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetWriteChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session, channel.ConnectionDescription); - return await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); } } diff --git a/src/MongoDB.Driver/Core/Operations/OperationExtensionMethods.cs b/src/MongoDB.Driver/Core/Operations/OperationExtensionMethods.cs index bf9fe96b58b..fe54eb7d637 100644 --- a/src/MongoDB.Driver/Core/Operations/OperationExtensionMethods.cs +++ b/src/MongoDB.Driver/Core/Operations/OperationExtensionMethods.cs @@ -13,7 +13,6 @@ * limitations under the License. */ -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Misc; @@ -24,55 +23,55 @@ internal static class OperationExtensionMethods { public static TResult Execute( this IReadOperation operation, + OperationContext operationContext, IChannelSourceHandle channelSource, ReadPreference readPreference, - ICoreSessionHandle session, - CancellationToken cancellationToken) + ICoreSessionHandle session) { Ensure.IsNotNull(operation, nameof(operation)); using (var readBinding = new ChannelSourceReadWriteBinding(channelSource.Fork(), readPreference, session.Fork())) { - return operation.Execute(readBinding, cancellationToken); + return operation.Execute(operationContext, readBinding); } } public static TResult Execute( this IWriteOperation operation, + OperationContext operationContext, IChannelSourceHandle channelSource, - ICoreSessionHandle session, - CancellationToken cancellationToken) + ICoreSessionHandle session) { Ensure.IsNotNull(operation, nameof(operation)); using (var writeBinding = new ChannelSourceReadWriteBinding(channelSource.Fork(), ReadPreference.Primary, session.Fork())) { - return operation.Execute(writeBinding, cancellationToken); + return operation.Execute(operationContext, writeBinding); } } public static async Task ExecuteAsync( this IReadOperation operation, + OperationContext operationContext, IChannelSourceHandle channelSource, ReadPreference readPreference, - ICoreSessionHandle session, - CancellationToken cancellationToken) + ICoreSessionHandle session) { Ensure.IsNotNull(operation, nameof(operation)); using (var readBinding = new ChannelSourceReadWriteBinding(channelSource.Fork(), readPreference, session.Fork())) { - return await operation.ExecuteAsync(readBinding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, readBinding).ConfigureAwait(false); } } public static async Task ExecuteAsync( this IWriteOperation operation, + OperationContext operationContext, IChannelSourceHandle channelSource, - ICoreSessionHandle session, - CancellationToken cancellationToken) + ICoreSessionHandle session) { Ensure.IsNotNull(operation, nameof(operation)); using (var writeBinding = new ChannelSourceReadWriteBinding(channelSource.Fork(), ReadPreference.Primary, session.Fork())) { - return await operation.ExecuteAsync(writeBinding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, writeBinding).ConfigureAwait(false); } } } diff --git a/src/MongoDB.Driver/Core/Operations/PingOperation.cs b/src/MongoDB.Driver/Core/Operations/PingOperation.cs deleted file mode 100644 index cf86b18243c..00000000000 --- a/src/MongoDB.Driver/Core/Operations/PingOperation.cs +++ /dev/null @@ -1,71 +0,0 @@ -/* Copyright 2013-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System.Threading; -using System.Threading.Tasks; -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Driver.Core.Bindings; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; - -namespace MongoDB.Driver.Core.Operations -{ - internal sealed class PingOperation : IReadOperation - { - private MessageEncoderSettings _messageEncoderSettings; - - public PingOperation(MessageEncoderSettings messageEncoderSettings) - { - _messageEncoderSettings = messageEncoderSettings; - } - - public MessageEncoderSettings MessageEncoderSettings - { - get { return _messageEncoderSettings; } - } - - public BsonDocument CreateCommand() - { - return new BsonDocument - { - { "ping", 1 } - }; - } - - public BsonDocument Execute(IReadBinding binding, CancellationToken cancellationToken) - { - Ensure.IsNotNull(binding, nameof(binding)); - var operation = CreateOperation(); - return operation.Execute(binding, cancellationToken); - } - - public async Task ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) - { - Ensure.IsNotNull(binding, nameof(binding)); - var operation = CreateOperation(); - return await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); - } - - private ReadCommandOperation CreateOperation() - { - var command = CreateCommand(); - return new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, _messageEncoderSettings) - { - RetryRequested = false - }; - } - } -} diff --git a/src/MongoDB.Driver/Core/Operations/ReadCommandOperation.cs b/src/MongoDB.Driver/Core/Operations/ReadCommandOperation.cs index 386bc77c4dd..84305df6f25 100644 --- a/src/MongoDB.Driver/Core/Operations/ReadCommandOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/ReadCommandOperation.cs @@ -13,7 +13,6 @@ * limitations under the License. */ -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization; @@ -43,54 +42,54 @@ public bool RetryRequested set => _retryRequested = value; } - public TCommandResult Execute(IReadBinding binding, CancellationToken cancellationToken) + public TCommandResult Execute(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); - using (var context = RetryableReadContext.Create(binding, _retryRequested, cancellationToken)) + using (var context = RetryableReadContext.Create(operationContext, binding, _retryRequested)) { - return Execute(context, cancellationToken); + return Execute(operationContext, context); } } - public TCommandResult Execute(RetryableReadContext context, CancellationToken cancellationToken) + public TCommandResult Execute(OperationContext operationContext, RetryableReadContext context) { Ensure.IsNotNull(context, nameof(context)); using (EventContext.BeginOperation()) { - return RetryableReadOperationExecutor.Execute(this, context, cancellationToken); + return RetryableReadOperationExecutor.Execute(operationContext, this, context); } } - public async Task ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IReadBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); - using (var context = await RetryableReadContext.CreateAsync(binding, _retryRequested, cancellationToken).ConfigureAwait(false)) + using (var context = await RetryableReadContext.CreateAsync(operationContext, binding, _retryRequested).ConfigureAwait(false)) { - return await ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + return await ExecuteAsync(operationContext, context).ConfigureAwait(false); } } - public async Task ExecuteAsync(RetryableReadContext context, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, RetryableReadContext context) { Ensure.IsNotNull(context, nameof(context)); using (EventContext.BeginOperation()) { - return await RetryableReadOperationExecutor.ExecuteAsync(this, context, cancellationToken).ConfigureAwait(false); + return await RetryableReadOperationExecutor.ExecuteAsync(operationContext, this, context).ConfigureAwait(false); } } - public TCommandResult ExecuteAttempt(RetryableReadContext context, int attempt, long? transactionNumber, CancellationToken cancellationToken) + public TCommandResult ExecuteAttempt(OperationContext operationContext, RetryableReadContext context, int attempt, long? transactionNumber) { - return ExecuteProtocol(context.Channel, context.Binding.Session, context.Binding.ReadPreference, cancellationToken); + return ExecuteProtocol(context.Channel, context.Binding.Session, context.Binding.ReadPreference, operationContext.CancellationToken); } - public Task ExecuteAttemptAsync(RetryableReadContext context, int attempt, long? transactionNumber, CancellationToken cancellationToken) + public Task ExecuteAttemptAsync(OperationContext operationContext, RetryableReadContext context, int attempt, long? transactionNumber) { - return ExecuteProtocolAsync(context.Channel, context.Binding.Session, context.Binding.ReadPreference, cancellationToken); + return ExecuteProtocolAsync(context.Channel, context.Binding.Session, context.Binding.ReadPreference, operationContext.CancellationToken); } } } diff --git a/src/MongoDB.Driver/Core/Operations/RenameCollectionOperation.cs b/src/MongoDB.Driver/Core/Operations/RenameCollectionOperation.cs index 7f5fb113da5..aee3f2fed1c 100644 --- a/src/MongoDB.Driver/Core/Operations/RenameCollectionOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/RenameCollectionOperation.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -83,34 +82,34 @@ public BsonDocument CreateCommand(ICoreSessionHandle session, ConnectionDescript }; } - public BsonDocument Execute(IWriteBinding binding, CancellationToken cancellationToken) + public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var channelSource = binding.GetWriteChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetWriteChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session, channel.ConnectionDescription); - return operation.Execute(channelBinding, cancellationToken); + return operation.Execute(operationContext, channelBinding); } } - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (BeginOperation()) - using (var channelSource = await binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetWriteChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(channelBinding.Session, channel.ConnectionDescription); - return await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); } - } - + } + private IDisposable BeginOperation() => EventContext.BeginOperation("renameCollection"); private WriteCommandOperation CreateOperation(ICoreSessionHandle session, ConnectionDescription connectionDescription) diff --git a/src/MongoDB.Driver/Core/Operations/RetryableReadContext.cs b/src/MongoDB.Driver/Core/Operations/RetryableReadContext.cs index 4593b1c06e8..18d62e4b4a5 100644 --- a/src/MongoDB.Driver/Core/Operations/RetryableReadContext.cs +++ b/src/MongoDB.Driver/Core/Operations/RetryableReadContext.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Misc; @@ -25,12 +24,12 @@ internal sealed class RetryableReadContext : IDisposable { #region static - public static RetryableReadContext Create(IReadBinding binding, bool retryRequested, CancellationToken cancellationToken) + public static RetryableReadContext Create(OperationContext operationContext, IReadBinding binding, bool retryRequested) { var context = new RetryableReadContext(binding, retryRequested); try { - context.Initialize(cancellationToken); + context.Initialize(operationContext); ChannelPinningHelper.PinChannellIfRequired( context.ChannelSource, @@ -46,12 +45,12 @@ public static RetryableReadContext Create(IReadBinding binding, bool retryReques } } - public static async Task CreateAsync(IReadBinding binding, bool retryRequested, CancellationToken cancellationToken) + public static async Task CreateAsync(OperationContext operationContext, IReadBinding binding, bool retryRequested) { var context = new RetryableReadContext(binding, retryRequested); try { - await context.InitializeAsync(cancellationToken).ConfigureAwait(false); + await context.InitializeAsync(operationContext).ConfigureAwait(false); ChannelPinningHelper.PinChannellIfRequired( context.ChannelSource, @@ -113,33 +112,33 @@ public void ReplaceChannelSource(IChannelSourceHandle channelSource) _channel = null; } - private void Initialize(CancellationToken cancellationToken) + private void Initialize(OperationContext operationContext) { - _channelSource = _binding.GetReadChannelSource(cancellationToken); + _channelSource = _binding.GetReadChannelSource(operationContext); try { - _channel = _channelSource.GetChannel(cancellationToken); + _channel = _channelSource.GetChannel(operationContext); } catch (Exception ex) when (RetryableReadOperationExecutor.ShouldConnectionAcquireBeRetried(this, ex)) { - ReplaceChannelSource(_binding.GetReadChannelSource(cancellationToken)); - ReplaceChannel(_channelSource.GetChannel(cancellationToken)); + ReplaceChannelSource(_binding.GetReadChannelSource(operationContext)); + ReplaceChannel(_channelSource.GetChannel(operationContext)); } } - private async Task InitializeAsync(CancellationToken cancellationToken) + private async Task InitializeAsync(OperationContext operationContext) { - _channelSource = await _binding.GetReadChannelSourceAsync(cancellationToken).ConfigureAwait(false); + _channelSource = await _binding.GetReadChannelSourceAsync(operationContext).ConfigureAwait(false); try { - _channel = await _channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false); + _channel = await _channelSource.GetChannelAsync(operationContext).ConfigureAwait(false); } catch (Exception ex) when (RetryableReadOperationExecutor.ShouldConnectionAcquireBeRetried(this, ex)) { - ReplaceChannelSource(await _binding.GetReadChannelSourceAsync(cancellationToken).ConfigureAwait(false)); - ReplaceChannel(await _channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)); + ReplaceChannelSource(await _binding.GetReadChannelSourceAsync(operationContext).ConfigureAwait(false)); + ReplaceChannel(await _channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)); } } } diff --git a/src/MongoDB.Driver/Core/Operations/RetryableReadOperationExecutor.cs b/src/MongoDB.Driver/Core/Operations/RetryableReadOperationExecutor.cs index cdb74827dc0..d628382e264 100644 --- a/src/MongoDB.Driver/Core/Operations/RetryableReadOperationExecutor.cs +++ b/src/MongoDB.Driver/Core/Operations/RetryableReadOperationExecutor.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Bindings; @@ -23,25 +22,25 @@ namespace MongoDB.Driver.Core.Operations internal static class RetryableReadOperationExecutor { // public static methods - public static TResult Execute(IRetryableReadOperation operation, IReadBinding binding, bool retryRequested, CancellationToken cancellationToken) + public static TResult Execute(OperationContext operationContext, IRetryableReadOperation operation, IReadBinding binding, bool retryRequested) { - using (var context = RetryableReadContext.Create(binding, retryRequested, cancellationToken)) + using (var context = RetryableReadContext.Create(operationContext, binding, retryRequested)) { - return Execute(operation, context, cancellationToken); + return Execute(operationContext, operation, context); } } - public static TResult Execute(IRetryableReadOperation operation, RetryableReadContext context, CancellationToken cancellationToken) + public static TResult Execute(OperationContext operationContext, IRetryableReadOperation operation, RetryableReadContext context) { if (!ShouldReadBeRetried(context)) { - return operation.ExecuteAttempt(context, attempt: 1, transactionNumber: null, cancellationToken); + return operation.ExecuteAttempt(operationContext, context, attempt: 1, transactionNumber: null); } Exception originalException; try { - return operation.ExecuteAttempt(context, attempt: 1, transactionNumber: null, cancellationToken); + return operation.ExecuteAttempt(operationContext, context, attempt: 1, transactionNumber: null); } catch (Exception ex) when (RetryabilityHelper.IsRetryableReadException(ex)) @@ -51,8 +50,8 @@ public static TResult Execute(IRetryableReadOperation operatio try { - context.ReplaceChannelSource(context.Binding.GetReadChannelSource(new[] { context.ChannelSource.ServerDescription }, cancellationToken)); - context.ReplaceChannel(context.ChannelSource.GetChannel(cancellationToken)); + context.ReplaceChannelSource(context.Binding.GetReadChannelSource(operationContext, new[] { context.ChannelSource.ServerDescription })); + context.ReplaceChannel(context.ChannelSource.GetChannel(operationContext)); } catch { @@ -61,7 +60,7 @@ public static TResult Execute(IRetryableReadOperation operatio try { - return operation.ExecuteAttempt(context, attempt: 2, transactionNumber: null, cancellationToken); + return operation.ExecuteAttempt(operationContext, context, attempt: 2, transactionNumber: null); } catch (Exception ex) when (ShouldThrowOriginalException(ex)) { @@ -69,25 +68,25 @@ public static TResult Execute(IRetryableReadOperation operatio } } - public static async Task ExecuteAsync(IRetryableReadOperation operation, IReadBinding binding, bool retryRequested, CancellationToken cancellationToken) + public static async Task ExecuteAsync(OperationContext operationContext, IRetryableReadOperation operation, IReadBinding binding, bool retryRequested) { - using (var context = await RetryableReadContext.CreateAsync(binding, retryRequested, cancellationToken).ConfigureAwait(false)) + using (var context = await RetryableReadContext.CreateAsync(operationContext, binding, retryRequested).ConfigureAwait(false)) { - return await ExecuteAsync(operation, context, cancellationToken).ConfigureAwait(false); + return await ExecuteAsync(operationContext, operation, context).ConfigureAwait(false); } } - public static async Task ExecuteAsync(IRetryableReadOperation operation, RetryableReadContext context, CancellationToken cancellationToken) + public static async Task ExecuteAsync(OperationContext operationContext, IRetryableReadOperation operation, RetryableReadContext context) { if (!ShouldReadBeRetried(context)) { - return await operation.ExecuteAttemptAsync(context, attempt: 1, transactionNumber: null, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAttemptAsync(operationContext, context, attempt: 1, transactionNumber: null).ConfigureAwait(false); } Exception originalException; try { - return await operation.ExecuteAttemptAsync(context, attempt: 1, transactionNumber: null, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAttemptAsync(operationContext, context, attempt: 1, transactionNumber: null).ConfigureAwait(false); } catch (Exception ex) when (RetryabilityHelper.IsRetryableReadException(ex)) { @@ -96,8 +95,8 @@ public static async Task ExecuteAsync(IRetryableReadOperation< try { - context.ReplaceChannelSource(context.Binding.GetReadChannelSource(new[] { context.ChannelSource.ServerDescription }, cancellationToken)); - context.ReplaceChannel(context.ChannelSource.GetChannel(cancellationToken)); + context.ReplaceChannelSource(context.Binding.GetReadChannelSource(operationContext, new[] { context.ChannelSource.ServerDescription })); + context.ReplaceChannel(context.ChannelSource.GetChannel(operationContext)); } catch { @@ -106,7 +105,7 @@ public static async Task ExecuteAsync(IRetryableReadOperation< try { - return await operation.ExecuteAttemptAsync(context, attempt: 2, transactionNumber: null, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAttemptAsync(operationContext, context, attempt: 2, transactionNumber: null).ConfigureAwait(false); } catch (Exception ex) when (ShouldThrowOriginalException(ex)) { diff --git a/src/MongoDB.Driver/Core/Operations/RetryableWriteCommandOperationBase.cs b/src/MongoDB.Driver/Core/Operations/RetryableWriteCommandOperationBase.cs index cd5e3e87dc9..bb3ec94e32d 100644 --- a/src/MongoDB.Driver/Core/Operations/RetryableWriteCommandOperationBase.cs +++ b/src/MongoDB.Driver/Core/Operations/RetryableWriteCommandOperationBase.cs @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.IO; @@ -87,36 +86,36 @@ public WriteConcern WriteConcern set { _writeConcern = value; } } - public virtual BsonDocument Execute(IWriteBinding binding, CancellationToken cancellationToken) + public virtual BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) { - using (var context = RetryableWriteContext.Create(binding, _retryRequested, cancellationToken)) + using (var context = RetryableWriteContext.Create(operationContext, binding, _retryRequested)) { - return Execute(context, cancellationToken); + return Execute(operationContext, context); } } - public virtual BsonDocument Execute(RetryableWriteContext context, CancellationToken cancellationToken) + public virtual BsonDocument Execute(OperationContext operationContext, RetryableWriteContext context) { - return RetryableWriteOperationExecutor.Execute(this, context, cancellationToken); + return RetryableWriteOperationExecutor.Execute(operationContext, this, context); } - public virtual async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public virtual async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { - using (var context = await RetryableWriteContext.CreateAsync(binding, _retryRequested, cancellationToken).ConfigureAwait(false)) + using (var context = await RetryableWriteContext.CreateAsync(operationContext, binding, _retryRequested).ConfigureAwait(false)) { - return await ExecuteAsync(context, cancellationToken).ConfigureAwait(false); + return await ExecuteAsync(operationContext, context).ConfigureAwait(false); } } - public virtual Task ExecuteAsync(RetryableWriteContext context, CancellationToken cancellationToken) + public virtual Task ExecuteAsync(OperationContext operationContext, RetryableWriteContext context) { - return RetryableWriteOperationExecutor.ExecuteAsync(this, context, cancellationToken); + return RetryableWriteOperationExecutor.ExecuteAsync(operationContext, this, context); } - public BsonDocument ExecuteAttempt(RetryableWriteContext context, int attempt, long? transactionNumber, CancellationToken cancellationToken) + public BsonDocument ExecuteAttempt(OperationContext operationContext, RetryableWriteContext context, int attempt, long? transactionNumber) { var args = GetCommandArgs(context, attempt, transactionNumber); - + // TODO: CSOT implement timeout in Command Execution return context.Channel.Command( context.ChannelSource.Session, ReadPreference.Primary, @@ -129,13 +128,13 @@ public BsonDocument ExecuteAttempt(RetryableWriteContext context, int attempt, l args.ResponseHandling, BsonDocumentSerializer.Instance, args.MessageEncoderSettings, - cancellationToken); + operationContext.CancellationToken); } - public Task ExecuteAttemptAsync(RetryableWriteContext context, int attempt, long? transactionNumber, CancellationToken cancellationToken) + public Task ExecuteAttemptAsync(OperationContext operationContext, RetryableWriteContext context, int attempt, long? transactionNumber) { var args = GetCommandArgs(context, attempt, transactionNumber); - + // TODO: CSOT implement timeout in Command Execution return context.Channel.CommandAsync( context.ChannelSource.Session, ReadPreference.Primary, @@ -148,7 +147,7 @@ public Task ExecuteAttemptAsync(RetryableWriteContext context, int args.ResponseHandling, BsonDocumentSerializer.Instance, args.MessageEncoderSettings, - cancellationToken); + operationContext.CancellationToken); } protected abstract BsonDocument CreateCommand(ICoreSessionHandle session, int attempt, long? transactionNumber); diff --git a/src/MongoDB.Driver/Core/Operations/RetryableWriteContext.cs b/src/MongoDB.Driver/Core/Operations/RetryableWriteContext.cs index 4aa1a3eb9fd..9c15d1e9bfc 100644 --- a/src/MongoDB.Driver/Core/Operations/RetryableWriteContext.cs +++ b/src/MongoDB.Driver/Core/Operations/RetryableWriteContext.cs @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Misc; @@ -27,12 +26,12 @@ internal sealed class RetryableWriteContext : IDisposable { #region static - public static RetryableWriteContext Create(IWriteBinding binding, bool retryRequested, CancellationToken cancellationToken) + public static RetryableWriteContext Create(OperationContext operationContext, IWriteBinding binding, bool retryRequested) { var context = new RetryableWriteContext(binding, retryRequested); try { - context.Initialize(cancellationToken); + context.Initialize(operationContext); ChannelPinningHelper.PinChannellIfRequired( context.ChannelSource, @@ -48,12 +47,12 @@ public static RetryableWriteContext Create(IWriteBinding binding, bool retryRequ } } - public static async Task CreateAsync(IWriteBinding binding, bool retryRequested, CancellationToken cancellationToken) + public static async Task CreateAsync(OperationContext operationContext, IWriteBinding binding, bool retryRequested) { var context = new RetryableWriteContext(binding, retryRequested); try { - await context.InitializeAsync(cancellationToken).ConfigureAwait(false); + await context.InitializeAsync(operationContext).ConfigureAwait(false); ChannelPinningHelper.PinChannellIfRequired( context.ChannelSource, @@ -126,35 +125,35 @@ public void ReplaceChannelSource(IChannelSourceHandle channelSource) _channel = null; } - private void Initialize(CancellationToken cancellationToken) + private void Initialize(OperationContext operationContext) { - _channelSource = _binding.GetWriteChannelSource(cancellationToken); + _channelSource = _binding.GetWriteChannelSource(operationContext); var serverDescription = _channelSource.ServerDescription; try { - _channel = _channelSource.GetChannel(cancellationToken); + _channel = _channelSource.GetChannel(operationContext); } catch (Exception ex) when (RetryableWriteOperationExecutor.ShouldConnectionAcquireBeRetried(this, serverDescription, ex)) { - ReplaceChannelSource(_binding.GetWriteChannelSource(cancellationToken)); - ReplaceChannel(_channelSource.GetChannel(cancellationToken)); + ReplaceChannelSource(_binding.GetWriteChannelSource(operationContext)); + ReplaceChannel(_channelSource.GetChannel(operationContext)); } } - private async Task InitializeAsync(CancellationToken cancellationToken) + private async Task InitializeAsync(OperationContext operationContext) { - _channelSource = await _binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false); + _channelSource = await _binding.GetWriteChannelSourceAsync(operationContext).ConfigureAwait(false); var serverDescription = _channelSource.ServerDescription; try { - _channel = await _channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false); + _channel = await _channelSource.GetChannelAsync(operationContext).ConfigureAwait(false); } catch (Exception ex) when (RetryableWriteOperationExecutor.ShouldConnectionAcquireBeRetried(this, serverDescription, ex)) { - ReplaceChannelSource(await _binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false)); - ReplaceChannel(await _channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)); + ReplaceChannelSource(await _binding.GetWriteChannelSourceAsync(operationContext).ConfigureAwait(false)); + ReplaceChannel(await _channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)); } } } diff --git a/src/MongoDB.Driver/Core/Operations/RetryableWriteOperationExecutor.cs b/src/MongoDB.Driver/Core/Operations/RetryableWriteOperationExecutor.cs index 6e73af1e758..f654d614260 100644 --- a/src/MongoDB.Driver/Core/Operations/RetryableWriteOperationExecutor.cs +++ b/src/MongoDB.Driver/Core/Operations/RetryableWriteOperationExecutor.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Connections; @@ -25,26 +24,26 @@ namespace MongoDB.Driver.Core.Operations internal static class RetryableWriteOperationExecutor { // public static methods - public static TResult Execute(IRetryableWriteOperation operation, IWriteBinding binding, bool retryRequested, CancellationToken cancellationToken) + public static TResult Execute(OperationContext operationContext, IRetryableWriteOperation operation, IWriteBinding binding, bool retryRequested) { - using (var context = RetryableWriteContext.Create(binding, retryRequested, cancellationToken)) + using (var context = RetryableWriteContext.Create(operationContext, binding, retryRequested)) { - return Execute(operation, context, cancellationToken); + return Execute(operationContext, operation, context); } } - public static TResult Execute(IRetryableWriteOperation operation, RetryableWriteContext context, CancellationToken cancellationToken) + public static TResult Execute(OperationContext operationContext, IRetryableWriteOperation operation, RetryableWriteContext context) { if (!AreRetriesAllowed(operation, context)) { - return operation.ExecuteAttempt(context, 1, null, cancellationToken); + return operation.ExecuteAttempt(operationContext, context, 1, null); } var transactionNumber = context.Binding.Session.AdvanceTransactionNumber(); Exception originalException; try { - return operation.ExecuteAttempt(context, 1, transactionNumber, cancellationToken); + return operation.ExecuteAttempt(operationContext, context, 1, transactionNumber); } catch (Exception ex) when (RetryabilityHelper.IsRetryableWriteException(ex)) { @@ -53,8 +52,8 @@ public static TResult Execute(IRetryableWriteOperation operati try { - context.ReplaceChannelSource(context.Binding.GetWriteChannelSource(new[] { context.ChannelSource.ServerDescription }, cancellationToken)); - context.ReplaceChannel(context.ChannelSource.GetChannel(cancellationToken)); + context.ReplaceChannelSource(context.Binding.GetWriteChannelSource(operationContext, new[] { context.ChannelSource.ServerDescription })); + context.ReplaceChannel(context.ChannelSource.GetChannel(operationContext)); } catch { @@ -68,7 +67,7 @@ public static TResult Execute(IRetryableWriteOperation operati try { - return operation.ExecuteAttempt(context, 2, transactionNumber, cancellationToken); + return operation.ExecuteAttempt(operationContext, context, 2, transactionNumber); } catch (Exception ex) when (ShouldThrowOriginalException(ex)) { @@ -76,26 +75,26 @@ public static TResult Execute(IRetryableWriteOperation operati } } - public async static Task ExecuteAsync(IRetryableWriteOperation operation, IWriteBinding binding, bool retryRequested, CancellationToken cancellationToken) + public async static Task ExecuteAsync(OperationContext operationContext, IRetryableWriteOperation operation, IWriteBinding binding, bool retryRequested) { - using (var context = await RetryableWriteContext.CreateAsync(binding, retryRequested, cancellationToken).ConfigureAwait(false)) + using (var context = await RetryableWriteContext.CreateAsync(operationContext, binding, retryRequested).ConfigureAwait(false)) { - return await ExecuteAsync(operation, context, cancellationToken).ConfigureAwait(false); + return await ExecuteAsync(operationContext, operation, context).ConfigureAwait(false); } } - public static async Task ExecuteAsync(IRetryableWriteOperation operation, RetryableWriteContext context, CancellationToken cancellationToken) + public static async Task ExecuteAsync(OperationContext operationContext, IRetryableWriteOperation operation, RetryableWriteContext context) { if (!AreRetriesAllowed(operation, context)) { - return await operation.ExecuteAttemptAsync(context, 1, null, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAttemptAsync(operationContext, context, 1, null).ConfigureAwait(false); } var transactionNumber = context.Binding.Session.AdvanceTransactionNumber(); Exception originalException; try { - return await operation.ExecuteAttemptAsync(context, 1, transactionNumber, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAttemptAsync(operationContext, context, 1, transactionNumber).ConfigureAwait(false); } catch (Exception ex) when (RetryabilityHelper.IsRetryableWriteException(ex)) { @@ -104,8 +103,8 @@ public static async Task ExecuteAsync(IRetryableWriteOperation try { - context.ReplaceChannelSource(await context.Binding.GetWriteChannelSourceAsync(new[] { context.ChannelSource.ServerDescription }, cancellationToken).ConfigureAwait(false)); - context.ReplaceChannel(await context.ChannelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)); + context.ReplaceChannelSource(await context.Binding.GetWriteChannelSourceAsync(operationContext, new[] { context.ChannelSource.ServerDescription }).ConfigureAwait(false)); + context.ReplaceChannel(await context.ChannelSource.GetChannelAsync(operationContext).ConfigureAwait(false)); } catch { @@ -119,7 +118,7 @@ public static async Task ExecuteAsync(IRetryableWriteOperation try { - return await operation.ExecuteAttemptAsync(context, 2, transactionNumber, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAttemptAsync(operationContext, context, 2, transactionNumber).ConfigureAwait(false); } catch (Exception ex) when (ShouldThrowOriginalException(ex)) { diff --git a/src/MongoDB.Driver/Core/Operations/UpdateSearchIndexOperation.cs b/src/MongoDB.Driver/Core/Operations/UpdateSearchIndexOperation.cs index 90657153792..09496dd9d3b 100644 --- a/src/MongoDB.Driver/Core/Operations/UpdateSearchIndexOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/UpdateSearchIndexOperation.cs @@ -13,7 +13,6 @@ * limitations under the License. */ -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -44,27 +43,27 @@ public UpdateSearchIndexOperation( _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); } - public BsonDocument Execute(IWriteBinding binding, CancellationToken cancellationToken) + public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) { using (EventContext.BeginOperation("updateSearchIndex")) - using (var channelSource = binding.GetWriteChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = binding.GetWriteChannelSource(operationContext)) + using (var channel = channelSource.GetChannel(operationContext)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(); - return operation.Execute(channelBinding, cancellationToken); + return operation.Execute(operationContext, channelBinding); } } - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { using (EventContext.BeginOperation("updateSearchIndex")) - using (var channelSource = await binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false)) - using (var channel = await channelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetWriteChannelSourceAsync(operationContext).ConfigureAwait(false)) + using (var channel = await channelSource.GetChannelAsync(operationContext).ConfigureAwait(false)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, binding.Session.Fork())) { var operation = CreateOperation(); - return await operation.ExecuteAsync(channelBinding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, channelBinding).ConfigureAwait(false); } } diff --git a/src/MongoDB.Driver/Core/Operations/WriteCommandOperation.cs b/src/MongoDB.Driver/Core/Operations/WriteCommandOperation.cs index 183efdf9888..033db2d0eae 100644 --- a/src/MongoDB.Driver/Core/Operations/WriteCommandOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/WriteCommandOperation.cs @@ -13,7 +13,6 @@ * limitations under the License. */ -using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization; @@ -39,25 +38,25 @@ public ReadPreference ReadPreference set => _readPreference = Ensure.IsNotNull(value, nameof(value)); } - public TCommandResult Execute(IWriteBinding binding, CancellationToken cancellationToken) + public TCommandResult Execute(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (EventContext.BeginOperation()) - using (var channelSource = binding.GetWriteChannelSource(cancellationToken)) + using (var channelSource = binding.GetWriteChannelSource(operationContext)) { - return ExecuteProtocol(channelSource, binding.Session, _readPreference, cancellationToken); + return ExecuteProtocol(operationContext, channelSource, binding.Session, _readPreference); } } - public async Task ExecuteAsync(IWriteBinding binding, CancellationToken cancellationToken = default(CancellationToken)) + public async Task ExecuteAsync(OperationContext operationContext, IWriteBinding binding) { Ensure.IsNotNull(binding, nameof(binding)); using (EventContext.BeginOperation()) - using (var channelSource = await binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false)) + using (var channelSource = await binding.GetWriteChannelSourceAsync(operationContext).ConfigureAwait(false)) { - return await ExecuteProtocolAsync(channelSource, binding.Session, _readPreference, cancellationToken).ConfigureAwait(false); + return await ExecuteProtocolAsync(operationContext, channelSource, binding.Session, _readPreference).ConfigureAwait(false); } } } diff --git a/src/MongoDB.Driver/Core/Servers/IServer.cs b/src/MongoDB.Driver/Core/Servers/IServer.cs index bbd9e0cbb80..da1e6f49138 100644 --- a/src/MongoDB.Driver/Core/Servers/IServer.cs +++ b/src/MongoDB.Driver/Core/Servers/IServer.cs @@ -29,8 +29,8 @@ internal interface IServer EndPoint EndPoint { get; } ServerId ServerId { get; } - IChannelHandle GetChannel(CancellationToken cancellationToken); - Task GetChannelAsync(CancellationToken cancellationToken); + IChannelHandle GetChannel(OperationContext operationContext); + Task GetChannelAsync(OperationContext operationContext); } internal interface IClusterableServer : IServer, IDisposable diff --git a/src/MongoDB.Driver/Core/Servers/Server.cs b/src/MongoDB.Driver/Core/Servers/Server.cs index e428c50212c..fa93175f1c0 100644 --- a/src/MongoDB.Driver/Core/Servers/Server.cs +++ b/src/MongoDB.Driver/Core/Servers/Server.cs @@ -107,7 +107,7 @@ public void Dispose() public void HandleExceptionOnOpen(Exception exception) => HandleBeforeHandshakeCompletesException(exception); - public IChannelHandle GetChannel(CancellationToken cancellationToken) + public IChannelHandle GetChannel(OperationContext operationContext) { ThrowIfNotOpen(); @@ -115,7 +115,7 @@ public IChannelHandle GetChannel(CancellationToken cancellationToken) { Interlocked.Increment(ref _outstandingOperationsCount); - var connection = _connectionPool.AcquireConnection(cancellationToken); + var connection = _connectionPool.AcquireConnection(operationContext); return new ServerChannel(this, connection); } catch @@ -126,14 +126,14 @@ public IChannelHandle GetChannel(CancellationToken cancellationToken) } } - public async Task GetChannelAsync(CancellationToken cancellationToken) + public async Task GetChannelAsync(OperationContext operationContext) { ThrowIfNotOpen(); try { Interlocked.Increment(ref _outstandingOperationsCount); - var connection = await _connectionPool.AcquireConnectionAsync(cancellationToken).ConfigureAwait(false); + var connection = await _connectionPool.AcquireConnectionAsync(operationContext).ConfigureAwait(false); return new ServerChannel(this, connection); } catch diff --git a/src/MongoDB.Driver/GridFS/GridFSBucket.cs b/src/MongoDB.Driver/GridFS/GridFSBucket.cs index 262939e4f85..7bd029044b8 100644 --- a/src/MongoDB.Driver/GridFS/GridFSBucket.cs +++ b/src/MongoDB.Driver/GridFS/GridFSBucket.cs @@ -83,13 +83,15 @@ public ImmutableGridFSBucketOptions Options public void Delete(TFileId id, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull((object)id, nameof(id)); - using (var binding = GetSingleServerReadWriteBinding(cancellationToken)) + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); + using (var binding = GetSingleServerReadWriteBinding(operationContext)) { var filesCollectionDeleteOperation = CreateDeleteFileOperation(id); - var filesCollectionDeleteResult = filesCollectionDeleteOperation.Execute(binding, cancellationToken); + var filesCollectionDeleteResult = filesCollectionDeleteOperation.Execute(operationContext, binding); var chunksDeleteOperation = CreateDeleteChunksOperation(id); - chunksDeleteOperation.Execute(binding, cancellationToken); + chunksDeleteOperation.Execute(operationContext, binding); if (filesCollectionDeleteResult.DeletedCount == 0) { @@ -102,13 +104,15 @@ public ImmutableGridFSBucketOptions Options public async Task DeleteAsync(TFileId id, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull((object)id, nameof(id)); - using (var binding = await GetSingleServerReadWriteBindingAsync(cancellationToken).ConfigureAwait(false)) + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); + using (var binding = await GetSingleServerReadWriteBindingAsync(operationContext).ConfigureAwait(false)) { var filesCollectionDeleteOperation = CreateDeleteFileOperation(id); - var filesCollectionDeleteResult = await filesCollectionDeleteOperation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); + var filesCollectionDeleteResult = await filesCollectionDeleteOperation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); var chunksDeleteOperation = CreateDeleteChunksOperation(id); - await chunksDeleteOperation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); + await chunksDeleteOperation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); if (filesCollectionDeleteResult.DeletedCount == 0) { @@ -121,10 +125,12 @@ public ImmutableGridFSBucketOptions Options public byte[] DownloadAsBytes(TFileId id, GridFSDownloadOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull((object)id, nameof(id)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSDownloadOptions(); - using (var binding = GetSingleServerReadBinding(cancellationToken)) + using (var binding = GetSingleServerReadBinding(operationContext)) { - var fileInfo = GetFileInfo(binding, id, cancellationToken); + var fileInfo = GetFileInfo(operationContext, binding, id); return DownloadAsBytesHelper(binding, fileInfo, options, cancellationToken); } } @@ -133,10 +139,12 @@ public ImmutableGridFSBucketOptions Options public async Task DownloadAsBytesAsync(TFileId id, GridFSDownloadOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull((object)id, nameof(id)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSDownloadOptions(); - using (var binding = await GetSingleServerReadBindingAsync(cancellationToken).ConfigureAwait(false)) + using (var binding = await GetSingleServerReadBindingAsync(operationContext).ConfigureAwait(false)) { - var fileInfo = await GetFileInfoAsync(binding, id, cancellationToken).ConfigureAwait(false); + var fileInfo = await GetFileInfoAsync(operationContext, binding, id).ConfigureAwait(false); return await DownloadAsBytesHelperAsync(binding, fileInfo, options, cancellationToken).ConfigureAwait(false); } } @@ -145,11 +153,13 @@ public ImmutableGridFSBucketOptions Options public byte[] DownloadAsBytesByName(string filename, GridFSDownloadByNameOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull(filename, nameof(filename)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSDownloadByNameOptions(); - using (var binding = GetSingleServerReadBinding(cancellationToken)) + using (var binding = GetSingleServerReadBinding(operationContext)) { - var fileInfo = GetFileInfoByName(binding, filename, options.Revision, cancellationToken); + var fileInfo = GetFileInfoByName(operationContext, binding, filename, options.Revision); return DownloadAsBytesHelper(binding, fileInfo, options, cancellationToken); } } @@ -158,11 +168,13 @@ public ImmutableGridFSBucketOptions Options public async Task DownloadAsBytesByNameAsync(string filename, GridFSDownloadByNameOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull(filename, nameof(filename)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSDownloadByNameOptions(); - using (var binding = await GetSingleServerReadBindingAsync(cancellationToken).ConfigureAwait(false)) + using (var binding = await GetSingleServerReadBindingAsync(operationContext).ConfigureAwait(false)) { - var fileInfo = await GetFileInfoByNameAsync(binding, filename, options.Revision, cancellationToken).ConfigureAwait(false); + var fileInfo = await GetFileInfoByNameAsync(operationContext, binding, filename, options.Revision).ConfigureAwait(false); return await DownloadAsBytesHelperAsync(binding, fileInfo, options, cancellationToken).ConfigureAwait(false); } } @@ -172,10 +184,12 @@ public ImmutableGridFSBucketOptions Options { Ensure.IsNotNull((object)id, nameof(id)); Ensure.IsNotNull(destination, nameof(destination)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSDownloadOptions(); - using (var binding = GetSingleServerReadBinding(cancellationToken)) + using (var binding = GetSingleServerReadBinding(operationContext)) { - var fileInfo = GetFileInfo(binding, id, cancellationToken); + var fileInfo = GetFileInfo(operationContext, binding, id); DownloadToStreamHelper(binding, fileInfo, destination, options, cancellationToken); } } @@ -185,10 +199,12 @@ public ImmutableGridFSBucketOptions Options { Ensure.IsNotNull((object)id, nameof(id)); Ensure.IsNotNull(destination, nameof(destination)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSDownloadOptions(); - using (var binding = await GetSingleServerReadBindingAsync(cancellationToken).ConfigureAwait(false)) + using (var binding = await GetSingleServerReadBindingAsync(operationContext).ConfigureAwait(false)) { - var fileInfo = await GetFileInfoAsync(binding, id, cancellationToken).ConfigureAwait(false); + var fileInfo = await GetFileInfoAsync(operationContext, binding, id).ConfigureAwait(false); await DownloadToStreamHelperAsync(binding, fileInfo, destination, options, cancellationToken).ConfigureAwait(false); } } @@ -198,11 +214,13 @@ public ImmutableGridFSBucketOptions Options { Ensure.IsNotNull(filename, nameof(filename)); Ensure.IsNotNull(destination, nameof(destination)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSDownloadByNameOptions(); - using (var binding = GetSingleServerReadBinding(cancellationToken)) + using (var binding = GetSingleServerReadBinding(operationContext)) { - var fileInfo = GetFileInfoByName(binding, filename, options.Revision, cancellationToken); + var fileInfo = GetFileInfoByName(operationContext, binding, filename, options.Revision); DownloadToStreamHelper(binding, fileInfo, destination, options, cancellationToken); } } @@ -212,11 +230,13 @@ public ImmutableGridFSBucketOptions Options { Ensure.IsNotNull(filename, nameof(filename)); Ensure.IsNotNull(destination, nameof(destination)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSDownloadByNameOptions(); - using (var binding = await GetSingleServerReadBindingAsync(cancellationToken).ConfigureAwait(false)) + using (var binding = await GetSingleServerReadBindingAsync(operationContext).ConfigureAwait(false)) { - var fileInfo = await GetFileInfoByNameAsync(binding, filename, options.Revision, cancellationToken).ConfigureAwait(false); + var fileInfo = await GetFileInfoByNameAsync(operationContext, binding, filename, options.Revision).ConfigureAwait(false); await DownloadToStreamHelperAsync(binding, fileInfo, destination, options, cancellationToken).ConfigureAwait(false); } } @@ -224,34 +244,38 @@ public ImmutableGridFSBucketOptions Options /// public void Drop(CancellationToken cancellationToken = default(CancellationToken)) { + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); var filesCollectionNamespace = this.GetFilesCollectionNamespace(); var chunksCollectionNamespace = this.GetChunksCollectionNamespace(); var messageEncoderSettings = this.GetMessageEncoderSettings(); - using (var binding = GetSingleServerReadWriteBinding(cancellationToken)) + using (var binding = GetSingleServerReadWriteBinding(operationContext)) { var filesCollectionDropOperation = CreateDropCollectionOperation(filesCollectionNamespace, messageEncoderSettings); - filesCollectionDropOperation.Execute(binding, cancellationToken); + filesCollectionDropOperation.Execute(operationContext, binding); var chunksCollectionDropOperation = CreateDropCollectionOperation(chunksCollectionNamespace, messageEncoderSettings); - chunksCollectionDropOperation.Execute(binding, cancellationToken); + chunksCollectionDropOperation.Execute(operationContext, binding); } } /// public async Task DropAsync(CancellationToken cancellationToken = default(CancellationToken)) { + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); var filesCollectionNamespace = this.GetFilesCollectionNamespace(); var chunksCollectionNamespace = this.GetChunksCollectionNamespace(); var messageEncoderSettings = this.GetMessageEncoderSettings(); - using (var binding = await GetSingleServerReadWriteBindingAsync(cancellationToken).ConfigureAwait(false)) + using (var binding = await GetSingleServerReadWriteBindingAsync(operationContext).ConfigureAwait(false)) { var filesCollectionDropOperation = CreateDropCollectionOperation(filesCollectionNamespace, messageEncoderSettings); - await filesCollectionDropOperation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); + await filesCollectionDropOperation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); var chunksCollectionDropOperation = CreateDropCollectionOperation(chunksCollectionNamespace, messageEncoderSettings); - await chunksCollectionDropOperation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); + await chunksCollectionDropOperation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); } } @@ -259,13 +283,15 @@ public ImmutableGridFSBucketOptions Options public IAsyncCursor> Find(FilterDefinition> filter, GridFSFindOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull(filter, nameof(filter)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSFindOptions(); var translationOptions = _database.Client.Settings.TranslationOptions; var operation = CreateFindOperation(filter, options, translationOptions); - using (var binding = GetSingleServerReadBinding(cancellationToken)) + using (var binding = GetSingleServerReadBinding(operationContext)) { - return operation.Execute(binding, cancellationToken); + return operation.Execute(operationContext, binding); } } @@ -273,13 +299,15 @@ public ImmutableGridFSBucketOptions Options public async Task>> FindAsync(FilterDefinition> filter, GridFSFindOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull(filter, nameof(filter)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSFindOptions(); var translationOptions = _database.Client.Settings.TranslationOptions; var operation = CreateFindOperation(filter, options, translationOptions); - using (var binding = await GetSingleServerReadBindingAsync(cancellationToken).ConfigureAwait(false)) + using (var binding = await GetSingleServerReadBindingAsync(operationContext).ConfigureAwait(false)) { - return await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); } } @@ -287,10 +315,12 @@ public ImmutableGridFSBucketOptions Options public GridFSDownloadStream OpenDownloadStream(TFileId id, GridFSDownloadOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull((object)id, nameof(id)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSDownloadOptions(); - using (var binding = GetSingleServerReadBinding(cancellationToken)) + using (var binding = GetSingleServerReadBinding(operationContext)) { - var fileInfo = GetFileInfo(binding, id, cancellationToken); + var fileInfo = GetFileInfo(operationContext, binding, id); return CreateDownloadStream(binding.Fork(), fileInfo, options, cancellationToken); } } @@ -299,10 +329,12 @@ public ImmutableGridFSBucketOptions Options public async Task> OpenDownloadStreamAsync(TFileId id, GridFSDownloadOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull((object)id, nameof(id)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSDownloadOptions(); - using (var binding = await GetSingleServerReadBindingAsync(cancellationToken).ConfigureAwait(false)) + using (var binding = await GetSingleServerReadBindingAsync(operationContext).ConfigureAwait(false)) { - var fileInfo = await GetFileInfoAsync(binding, id, cancellationToken).ConfigureAwait(false); + var fileInfo = await GetFileInfoAsync(operationContext, binding, id).ConfigureAwait(false); return CreateDownloadStream(binding.Fork(), fileInfo, options, cancellationToken); } } @@ -311,11 +343,13 @@ public ImmutableGridFSBucketOptions Options public GridFSDownloadStream OpenDownloadStreamByName(string filename, GridFSDownloadByNameOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull(filename, nameof(filename)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSDownloadByNameOptions(); - using (var binding = GetSingleServerReadBinding(cancellationToken)) + using (var binding = GetSingleServerReadBinding(operationContext)) { - var fileInfo = GetFileInfoByName(binding, filename, options.Revision, cancellationToken); + var fileInfo = GetFileInfoByName(operationContext, binding, filename, options.Revision); return CreateDownloadStream(binding.Fork(), fileInfo, options); } } @@ -324,11 +358,13 @@ public ImmutableGridFSBucketOptions Options public async Task> OpenDownloadStreamByNameAsync(string filename, GridFSDownloadByNameOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.IsNotNull(filename, nameof(filename)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSDownloadByNameOptions(); - using (var binding = await GetSingleServerReadBindingAsync(cancellationToken).ConfigureAwait(false)) + using (var binding = await GetSingleServerReadBindingAsync(operationContext).ConfigureAwait(false)) { - var fileInfo = await GetFileInfoByNameAsync(binding, filename, options.Revision, cancellationToken).ConfigureAwait(false); + var fileInfo = await GetFileInfoByNameAsync(operationContext, binding, filename, options.Revision).ConfigureAwait(false); return CreateDownloadStream(binding.Fork(), fileInfo, options); } } @@ -338,11 +374,13 @@ public ImmutableGridFSBucketOptions Options { Ensure.IsNotNull((object)id, nameof(id)); Ensure.IsNotNull(filename, nameof(filename)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSUploadOptions(); - using (var binding = GetSingleServerReadWriteBinding(cancellationToken)) + using (var binding = GetSingleServerReadWriteBinding(operationContext)) { - EnsureIndexes(binding, cancellationToken); + EnsureIndexes(operationContext, binding); return CreateUploadStream(binding, id, filename, options); } } @@ -352,11 +390,13 @@ public ImmutableGridFSBucketOptions Options { Ensure.IsNotNull((object)id, nameof(id)); Ensure.IsNotNull(filename, nameof(filename)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSUploadOptions(); - using (var binding = await GetSingleServerReadWriteBindingAsync(cancellationToken).ConfigureAwait(false)) + using (var binding = await GetSingleServerReadWriteBindingAsync(operationContext).ConfigureAwait(false)) { - await EnsureIndexesAsync(binding, cancellationToken).ConfigureAwait(false); + await EnsureIndexesAsync(operationContext, binding).ConfigureAwait(false); return CreateUploadStream(binding, id, filename, options); } } @@ -366,10 +406,12 @@ public ImmutableGridFSBucketOptions Options { Ensure.IsNotNull((object)id, nameof(id)); Ensure.IsNotNull(newFilename, nameof(newFilename)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); var renameOperation = CreateRenameOperation(id, newFilename); - using (var binding = GetSingleServerReadWriteBinding(cancellationToken)) + using (var binding = GetSingleServerReadWriteBinding(operationContext)) { - var result = renameOperation.Execute(binding, cancellationToken); + var result = renameOperation.Execute(operationContext, binding); if (result.IsModifiedCountAvailable && result.ModifiedCount == 0) { @@ -383,10 +425,12 @@ public ImmutableGridFSBucketOptions Options { Ensure.IsNotNull((object)id, nameof(id)); Ensure.IsNotNull(newFilename, nameof(newFilename)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); var renameOperation = CreateRenameOperation(id, newFilename); - using (var binding = await GetSingleServerReadWriteBindingAsync(cancellationToken).ConfigureAwait(false)) + using (var binding = await GetSingleServerReadWriteBindingAsync(operationContext).ConfigureAwait(false)) { - var result = await renameOperation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); + var result = await renameOperation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); if (result.IsModifiedCountAvailable && result.ModifiedCount == 0) { @@ -401,6 +445,8 @@ public ImmutableGridFSBucketOptions Options Ensure.IsNotNull((object)id, nameof(id)); Ensure.IsNotNull(filename, nameof(filename)); Ensure.IsNotNull(source, nameof(source)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSUploadOptions(); using (var sourceStream = new MemoryStream(source)) @@ -415,6 +461,8 @@ public ImmutableGridFSBucketOptions Options Ensure.IsNotNull((object)id, nameof(id)); Ensure.IsNotNull(filename, nameof(filename)); Ensure.IsNotNull(source, nameof(source)); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); options = options ?? new GridFSUploadOptions(); using (var sourceStream = new MemoryStream(source)) @@ -522,28 +570,28 @@ private bool ChunksCollectionIndexesExist(List indexes) return IndexExists(indexes, key); } - private bool ChunksCollectionIndexesExist(IReadBindingHandle binding, CancellationToken cancellationToken) + private bool ChunksCollectionIndexesExist(OperationContext operationContext, IReadBindingHandle binding) { - var indexes = ListIndexes(binding, this.GetChunksCollectionNamespace(), cancellationToken); + var indexes = ListIndexes(operationContext, binding, this.GetChunksCollectionNamespace()); return ChunksCollectionIndexesExist(indexes); } - private async Task ChunksCollectionIndexesExistAsync(IReadBindingHandle binding, CancellationToken cancellationToken) + private async Task ChunksCollectionIndexesExistAsync(OperationContext operationContext, IReadBindingHandle binding) { - var indexes = await ListIndexesAsync(binding, this.GetChunksCollectionNamespace(), cancellationToken).ConfigureAwait(false); + var indexes = await ListIndexesAsync(operationContext, binding, this.GetChunksCollectionNamespace()).ConfigureAwait(false); return ChunksCollectionIndexesExist(indexes); } - private void CreateChunksCollectionIndexes(IReadWriteBindingHandle binding, CancellationToken cancellationToken) + private void CreateChunksCollectionIndexes(OperationContext operationContext, IReadWriteBindingHandle binding) { var operation = CreateCreateChunksCollectionIndexesOperation(); - operation.Execute(binding, cancellationToken); + operation.Execute(operationContext, binding); } - private async Task CreateChunksCollectionIndexesAsync(IReadWriteBindingHandle binding, CancellationToken cancellationToken) + private async Task CreateChunksCollectionIndexesAsync(OperationContext operationContext, IReadWriteBindingHandle binding) { var operation = CreateCreateChunksCollectionIndexesOperation(); - await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); + await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); } internal CreateIndexesOperation CreateCreateChunksCollectionIndexesOperation() @@ -608,16 +656,16 @@ private BulkMixedWriteOperation CreateDeleteFileOperation(TFileId id) this.GetMessageEncoderSettings()); } - private void CreateFilesCollectionIndexes(IReadWriteBindingHandle binding, CancellationToken cancellationToken) + private void CreateFilesCollectionIndexes(OperationContext operationContext, IReadWriteBindingHandle binding) { var operation = CreateCreateFilesCollectionIndexesOperation(); - operation.Execute(binding, cancellationToken); + operation.Execute(operationContext, binding); } - private async Task CreateFilesCollectionIndexesAsync(IReadWriteBindingHandle binding, CancellationToken cancellationToken) + private async Task CreateFilesCollectionIndexesAsync(OperationContext operationContext, IReadWriteBindingHandle binding) { var operation = CreateCreateFilesCollectionIndexesOperation(); - await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); + await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); } private FindOperation> CreateFindOperation( @@ -810,23 +858,23 @@ private GridFSUploadStream CreateUploadStream(IReadWriteBindingHandle b } } - private void EnsureIndexes(IReadWriteBindingHandle binding, CancellationToken cancellationToken) + private void EnsureIndexes(OperationContext operationContext, IReadWriteBindingHandle binding) { - _ensureIndexesSemaphore.Wait(cancellationToken); + _ensureIndexesSemaphore.Wait(operationContext.RemainingTimeout, operationContext.CancellationToken); try { if (!_ensureIndexesDone) { - var isFilesCollectionEmpty = IsFilesCollectionEmpty(binding, cancellationToken); + var isFilesCollectionEmpty = IsFilesCollectionEmpty(operationContext, binding); if (isFilesCollectionEmpty) { - if (!FilesCollectionIndexesExist(binding, cancellationToken)) + if (!FilesCollectionIndexesExist(operationContext, binding)) { - CreateFilesCollectionIndexes(binding, cancellationToken); + CreateFilesCollectionIndexes(operationContext, binding); } - if (!ChunksCollectionIndexesExist(binding, cancellationToken)) + if (!ChunksCollectionIndexesExist(operationContext, binding)) { - CreateChunksCollectionIndexes(binding, cancellationToken); + CreateChunksCollectionIndexes(operationContext, binding); } } @@ -839,23 +887,23 @@ private void EnsureIndexes(IReadWriteBindingHandle binding, CancellationToken ca } } - private async Task EnsureIndexesAsync(IReadWriteBindingHandle binding, CancellationToken cancellationToken) + private async Task EnsureIndexesAsync(OperationContext operationContext, IReadWriteBindingHandle binding) { - await _ensureIndexesSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + await _ensureIndexesSemaphore.WaitAsync(operationContext.RemainingTimeout, operationContext.CancellationToken).ConfigureAwait(false); try { if (!_ensureIndexesDone) { - var isFilesCollectionEmpty = await IsFilesCollectionEmptyAsync(binding, cancellationToken).ConfigureAwait(false); + var isFilesCollectionEmpty = await IsFilesCollectionEmptyAsync(operationContext, binding).ConfigureAwait(false); if (isFilesCollectionEmpty) { - if (!(await FilesCollectionIndexesExistAsync(binding, cancellationToken).ConfigureAwait(false))) + if (!(await FilesCollectionIndexesExistAsync(operationContext, binding).ConfigureAwait(false))) { - await CreateFilesCollectionIndexesAsync(binding, cancellationToken).ConfigureAwait(false); + await CreateFilesCollectionIndexesAsync(operationContext, binding).ConfigureAwait(false); } - if (!(await ChunksCollectionIndexesExistAsync(binding, cancellationToken).ConfigureAwait(false))) + if (!(await ChunksCollectionIndexesExistAsync(operationContext, binding).ConfigureAwait(false))) { - await CreateChunksCollectionIndexesAsync(binding, cancellationToken).ConfigureAwait(false); + await CreateChunksCollectionIndexesAsync(operationContext, binding).ConfigureAwait(false); } } @@ -874,24 +922,25 @@ private bool FilesCollectionIndexesExist(List indexes) return IndexExists(indexes, key); } - private bool FilesCollectionIndexesExist(IReadBindingHandle binding, CancellationToken cancellationToken) + private bool FilesCollectionIndexesExist(OperationContext operationContext, IReadBindingHandle binding) { - var indexes = ListIndexes(binding, this.GetFilesCollectionNamespace(), cancellationToken); + var indexes = ListIndexes(operationContext, binding, this.GetFilesCollectionNamespace()); return FilesCollectionIndexesExist(indexes); } - private async Task FilesCollectionIndexesExistAsync(IReadBindingHandle binding, CancellationToken cancellationToken) + private async Task FilesCollectionIndexesExistAsync(OperationContext operationContext, IReadBindingHandle binding) { - var indexes = await ListIndexesAsync(binding, this.GetFilesCollectionNamespace(), cancellationToken).ConfigureAwait(false); + var indexes = await ListIndexesAsync(operationContext, binding, this.GetFilesCollectionNamespace()).ConfigureAwait(false); return FilesCollectionIndexesExist(indexes); } - private GridFSFileInfo GetFileInfo(IReadBindingHandle binding, TFileId id, CancellationToken cancellationToken) + private GridFSFileInfo GetFileInfo(OperationContext operationContext, IReadBindingHandle binding, TFileId id) { var operation = CreateGetFileInfoOperation(id); - using (var cursor = operation.Execute(binding, cancellationToken)) + using (var cursor = operation.Execute(operationContext, binding)) { - var fileInfo = cursor.FirstOrDefault(cancellationToken); + // TODO: CSOT add a way to propagate cancellationContext into cursor methods. + var fileInfo = cursor.FirstOrDefault(operationContext.CancellationToken); if (fileInfo == null) { throw new GridFSFileNotFoundException(_idSerializationInfo.SerializeValue(id)); @@ -900,12 +949,13 @@ private GridFSFileInfo GetFileInfo(IReadBindingHandle binding, TFileId } } - private async Task> GetFileInfoAsync(IReadBindingHandle binding, TFileId id, CancellationToken cancellationToken) + private async Task> GetFileInfoAsync(OperationContext operationContext, IReadBindingHandle binding, TFileId id) { var operation = CreateGetFileInfoOperation(id); - using (var cursor = await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false)) + using (var cursor = await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false)) { - var fileInfo = await cursor.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + // TODO: CSOT add a way to propagate cancellationContext into cursor methods. + var fileInfo = await cursor.FirstOrDefaultAsync(operationContext.CancellationToken).ConfigureAwait(false); if (fileInfo == null) { throw new GridFSFileNotFoundException(_idSerializationInfo.SerializeValue(id)); @@ -914,12 +964,13 @@ private async Task> GetFileInfoAsync(IReadBindingHandle } } - private GridFSFileInfo GetFileInfoByName(IReadBindingHandle binding, string filename, int revision, CancellationToken cancellationToken) + private GridFSFileInfo GetFileInfoByName(OperationContext operationContext, IReadBindingHandle binding, string filename, int revision) { var operation = CreateGetFileInfoByNameOperation(filename, revision); - using (var cursor = operation.Execute(binding, cancellationToken)) + using (var cursor = operation.Execute(operationContext, binding)) { - var fileInfo = cursor.FirstOrDefault(cancellationToken); + // TODO: CSOT add a way to propagate cancellationContext into cursor methods. + var fileInfo = cursor.FirstOrDefault(operationContext.CancellationToken); if (fileInfo == null) { throw new GridFSFileNotFoundException(filename, revision); @@ -928,12 +979,13 @@ private GridFSFileInfo GetFileInfoByName(IReadBindingHandle binding, st } } - private async Task> GetFileInfoByNameAsync(IReadBindingHandle binding, string filename, int revision, CancellationToken cancellationToken) + private async Task> GetFileInfoByNameAsync(OperationContext operationContext, IReadBindingHandle binding, string filename, int revision) { var operation = CreateGetFileInfoByNameOperation(filename, revision); - using (var cursor = await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false)) + using (var cursor = await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false)) { - var fileInfo = await cursor.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + // TODO: CSOT add a way to propagate cancellationContext into cursor methods. + var fileInfo = await cursor.FirstOrDefaultAsync(operationContext.CancellationToken).ConfigureAwait(false); if (fileInfo == null) { throw new GridFSFileNotFoundException(filename, revision); @@ -947,36 +999,36 @@ private ReadConcern GetReadConcern() return _options.ReadConcern ?? _database.Settings.ReadConcern; } - private IReadBindingHandle GetSingleServerReadBinding(CancellationToken cancellationToken) + private IReadBindingHandle GetSingleServerReadBinding(OperationContext operationContext) { var readPreference = _options.ReadPreference ?? _database.Settings.ReadPreference; var selector = new ReadPreferenceServerSelector(readPreference); - var server = _cluster.SelectServer(selector, cancellationToken); + var server = _cluster.SelectServer(operationContext, selector); var binding = new SingleServerReadBinding(server, readPreference, NoCoreSession.NewHandle()); return new ReadBindingHandle(binding); } - private async Task GetSingleServerReadBindingAsync(CancellationToken cancellationToken) + private async Task GetSingleServerReadBindingAsync(OperationContext operationContext) { var readPreference = _options.ReadPreference ?? _database.Settings.ReadPreference; var selector = new ReadPreferenceServerSelector(readPreference); - var server = await _cluster.SelectServerAsync(selector, cancellationToken).ConfigureAwait(false); + var server = await _cluster.SelectServerAsync(operationContext, selector).ConfigureAwait(false); var binding = new SingleServerReadBinding(server, readPreference, NoCoreSession.NewHandle()); return new ReadBindingHandle(binding); } - private IReadWriteBindingHandle GetSingleServerReadWriteBinding(CancellationToken cancellationToken) + private IReadWriteBindingHandle GetSingleServerReadWriteBinding(OperationContext operationContext) { var selector = WritableServerSelector.Instance; - var server = _cluster.SelectServer(selector, cancellationToken); + var server = _cluster.SelectServer(operationContext, selector); var binding = new SingleServerReadWriteBinding(server, NoCoreSession.NewHandle()); return new ReadWriteBindingHandle(binding); } - private async Task GetSingleServerReadWriteBindingAsync(CancellationToken cancellationToken) + private async Task GetSingleServerReadWriteBindingAsync(OperationContext operationContext) { var selector = WritableServerSelector.Instance; - var server = await _cluster.SelectServerAsync(selector, cancellationToken).ConfigureAwait(false); + var server = await _cluster.SelectServerAsync(operationContext, selector).ConfigureAwait(false); var binding = new SingleServerReadWriteBinding(server, NoCoreSession.NewHandle()); return new ReadWriteBindingHandle(binding); } @@ -993,37 +1045,39 @@ private bool IndexExists(List indexes, BsonDocument key) return false; } - private bool IsFilesCollectionEmpty(IReadWriteBindingHandle binding, CancellationToken cancellationToken) + private bool IsFilesCollectionEmpty(OperationContext operationContext, IReadWriteBindingHandle binding) { var operation = CreateIsFilesCollectionEmptyOperation(); - using (var cursor = operation.Execute(binding, cancellationToken)) + using (var cursor = operation.Execute(operationContext, binding)) { - var firstOrDefault = cursor.FirstOrDefault(cancellationToken); + // TODO: CSOT add a way to propagate cancellationContext into cursor methods. + var firstOrDefault = cursor.FirstOrDefault(operationContext.CancellationToken); return firstOrDefault == null; } } - private async Task IsFilesCollectionEmptyAsync(IReadWriteBindingHandle binding, CancellationToken cancellationToken) + private async Task IsFilesCollectionEmptyAsync(OperationContext operationContext, IReadWriteBindingHandle binding) { var operation = CreateIsFilesCollectionEmptyOperation(); - using (var cursor = await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false)) + using (var cursor = await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false)) { - var firstOrDefault = await cursor.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + // TODO: CSOT add a way to propagate cancellationContext into cursor methods. + var firstOrDefault = await cursor.FirstOrDefaultAsync(operationContext.CancellationToken).ConfigureAwait(false); return firstOrDefault == null; } } - private List ListIndexes(IReadBinding binding, CollectionNamespace collectionNamespace, CancellationToken cancellationToken) + private List ListIndexes(OperationContext operationContext, IReadBinding binding, CollectionNamespace collectionNamespace) { var operation = CreateListIndexesOperation(collectionNamespace); - return operation.Execute(binding, cancellationToken).ToList(); + return operation.Execute(operationContext, binding).ToList(); } - private async Task> ListIndexesAsync(IReadBinding binding, CollectionNamespace collectionNamespace, CancellationToken cancellationToken) + private async Task> ListIndexesAsync(OperationContext operationContext, IReadBinding binding, CollectionNamespace collectionNamespace) { var operation = CreateListIndexesOperation(collectionNamespace); - var cursor = await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); - return await cursor.ToListAsync(cancellationToken).ConfigureAwait(false); + var cursor = await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); + return await cursor.ToListAsync(operationContext.CancellationToken).ConfigureAwait(false); } } } diff --git a/src/MongoDB.Driver/GridFS/GridFSForwardOnlyDownloadStream.cs b/src/MongoDB.Driver/GridFS/GridFSForwardOnlyDownloadStream.cs index 0f9c09b96fe..23f5e48f877 100644 --- a/src/MongoDB.Driver/GridFS/GridFSForwardOnlyDownloadStream.cs +++ b/src/MongoDB.Driver/GridFS/GridFSForwardOnlyDownloadStream.cs @@ -196,14 +196,18 @@ private FindOperation CreateFirstBatchOperation() private void GetFirstBatch(CancellationToken cancellationToken) { var operation = CreateFirstBatchOperation(); - _cursor = operation.Execute(Binding, cancellationToken); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); + _cursor = operation.Execute(operationContext, Binding); GetNextBatch(cancellationToken); } private async Task GetFirstBatchAsync(CancellationToken cancellationToken) { var operation = CreateFirstBatchOperation(); - _cursor = await operation.ExecuteAsync(Binding, cancellationToken).ConfigureAwait(false); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); + _cursor = await operation.ExecuteAsync(operationContext, Binding).ConfigureAwait(false); await GetNextBatchAsync(cancellationToken).ConfigureAwait(false); } diff --git a/src/MongoDB.Driver/GridFS/GridFSForwardOnlyUploadStream.cs b/src/MongoDB.Driver/GridFS/GridFSForwardOnlyUploadStream.cs index 388b60e1fe8..f77fc81f56f 100644 --- a/src/MongoDB.Driver/GridFS/GridFSForwardOnlyUploadStream.cs +++ b/src/MongoDB.Driver/GridFS/GridFSForwardOnlyUploadStream.cs @@ -122,7 +122,9 @@ public override long Position _aborted = true; var operation = CreateAbortOperation(); - operation.Execute(_binding, cancellationToken); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); + operation.Execute(operationContext, _binding); } public override async Task AbortAsync(CancellationToken cancellationToken = default(CancellationToken)) @@ -135,7 +137,9 @@ public override long Position _aborted = true; var operation = CreateAbortOperation(); - await operation.ExecuteAsync(_binding, cancellationToken).ConfigureAwait(false); + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); + await operation.ExecuteAsync(operationContext, _binding).ConfigureAwait(false); } public override void Close(CancellationToken cancellationToken) diff --git a/src/MongoDB.Driver/GridFS/GridFSSeekableDownloadStream.cs b/src/MongoDB.Driver/GridFS/GridFSSeekableDownloadStream.cs index afbe4a0b6b6..843412df7ef 100644 --- a/src/MongoDB.Driver/GridFS/GridFSSeekableDownloadStream.cs +++ b/src/MongoDB.Driver/GridFS/GridFSSeekableDownloadStream.cs @@ -16,8 +16,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; @@ -174,7 +172,9 @@ private FindOperation CreateGetChunkOperation(long n) private void GetChunk(long n, CancellationToken cancellationToken) { var operation = CreateGetChunkOperation(n); - using (var cursor = operation.Execute(Binding, cancellationToken)) + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); + using (var cursor = operation.Execute(operationContext, Binding)) { var documents = cursor.ToList(); _chunk = GetChunkHelper(n, documents); @@ -185,7 +185,9 @@ private void GetChunk(long n, CancellationToken cancellationToken) private async Task GetChunkAsync(long n, CancellationToken cancellationToken) { var operation = CreateGetChunkOperation(n); - using (var cursor = await operation.ExecuteAsync(Binding, cancellationToken).ConfigureAwait(false)) + // TODO: CSOT implement proper way to obtain the operationContext + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationToken); + using (var cursor = await operation.ExecuteAsync(operationContext, Binding).ConfigureAwait(false)) { var documents = await cursor.ToListAsync().ConfigureAwait(false); _chunk = GetChunkHelper(n, documents); diff --git a/src/MongoDB.Driver/MongoClient.cs b/src/MongoDB.Driver/MongoClient.cs index 55d91940952..0df21ba32cc 100644 --- a/src/MongoDB.Driver/MongoClient.cs +++ b/src/MongoDB.Driver/MongoClient.cs @@ -92,8 +92,9 @@ internal MongoClient(MongoClientSettings settings, Func(); _cluster = _settings.ClusterSource.Get(_settings.ToClusterKey()); _operationExecutor = _operationExecutorFactory(this); - _readOperationOptions = new(DefaultReadPreference: _settings.ReadPreference); - _writeOperationOptions = new(); + // TODO: CSOT populate the timeout from settings + _readOperationOptions = new(Timeout: Timeout.InfiniteTimeSpan, DefaultReadPreference: _settings.ReadPreference); + _writeOperationOptions = new(Timeout: Timeout.InfiniteTimeSpan); if (settings.AutoEncryptionOptions != null) { diff --git a/src/MongoDB.Driver/MongoCollectionImpl.cs b/src/MongoDB.Driver/MongoCollectionImpl.cs index 4b0b144e960..8ad05198a18 100644 --- a/src/MongoDB.Driver/MongoCollectionImpl.cs +++ b/src/MongoDB.Driver/MongoCollectionImpl.cs @@ -58,8 +58,9 @@ private MongoCollectionImpl(IMongoDatabase database, CollectionNamespace collect _documentSerializer = Ensure.IsNotNull(documentSerializer, nameof(documentSerializer)); _messageEncoderSettings = GetMessageEncoderSettings(); - _readOperationOptions = new(DefaultReadPreference: _settings.ReadPreference); - _writeOperationOptions = new(); + // TODO: CSOT populate the timeout from settings + _readOperationOptions = new(Timeout: Timeout.InfiniteTimeSpan, DefaultReadPreference: _settings.ReadPreference); + _writeOperationOptions = new(Timeout: Timeout.InfiniteTimeSpan); } // properties diff --git a/src/MongoDB.Driver/MongoDatabase.cs b/src/MongoDB.Driver/MongoDatabase.cs index cb1e0820dff..00a91ca35e5 100644 --- a/src/MongoDB.Driver/MongoDatabase.cs +++ b/src/MongoDB.Driver/MongoDatabase.cs @@ -50,9 +50,9 @@ public MongoDatabase(IMongoClient client, DatabaseNamespace databaseNamespace, M _settings = Ensure.IsNotNull(settings, nameof(settings)).Freeze(); _cluster = Ensure.IsNotNull(cluster, nameof(cluster)); _operationExecutor = Ensure.IsNotNull(operationExecutor, nameof(operationExecutor)); - - _readOperationOptions = new(DefaultReadPreference: _settings.ReadPreference); - _writeOperationOptions = new(); + // TODO: CSOT populate the timeout from settings + _readOperationOptions = new(Timeout: Timeout.InfiniteTimeSpan, DefaultReadPreference: _settings.ReadPreference); + _writeOperationOptions = new(Timeout: Timeout.InfiniteTimeSpan); } // public properties diff --git a/src/MongoDB.Driver/OperationContext.cs b/src/MongoDB.Driver/OperationContext.cs new file mode 100644 index 00000000000..d9c25405e24 --- /dev/null +++ b/src/MongoDB.Driver/OperationContext.cs @@ -0,0 +1,161 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +#if !NET6_0_OR_GREATER +using MongoDB.Driver.Core.Misc; +#endif + +namespace MongoDB.Driver +{ + internal sealed class OperationContext + { + // TODO: this static field is temporary here and will be removed in a future PRs in scope of CSOT. + public static readonly OperationContext NoTimeout = new(System.Threading.Timeout.InfiniteTimeSpan, CancellationToken.None); + + public OperationContext(TimeSpan timeout, CancellationToken cancellationToken) + : this(Stopwatch.StartNew(), timeout, cancellationToken) + { + } + + internal OperationContext(Stopwatch stopwatch, TimeSpan timeout, CancellationToken cancellationToken) + { + Stopwatch = stopwatch; + Timeout = timeout; + CancellationToken = cancellationToken; + } + + public CancellationToken CancellationToken { get; } + + public OperationContext ParentContext { get; private init; } + + public TimeSpan RemainingTimeout + { + get + { + if (Timeout == System.Threading.Timeout.InfiniteTimeSpan) + { + return System.Threading.Timeout.InfiniteTimeSpan; + } + + return Timeout - Stopwatch.Elapsed; + } + } + + private Stopwatch Stopwatch { get; } + + public TimeSpan Timeout { get; } + + public bool IsTimedOut() + { + var remainingTimeout = RemainingTimeout; + if (remainingTimeout == System.Threading.Timeout.InfiniteTimeSpan) + { + return false; + } + + return remainingTimeout < TimeSpan.Zero; + } + + public void ThrowIfTimedOutOrCanceled() + { + CancellationToken.ThrowIfCancellationRequested(); + if (IsTimedOut()) + { + throw new TimeoutException(); + } + } + + public void WaitTask(Task task) + { + if (task.IsCompleted) + { + task.GetAwaiter().GetResult(); // re-throws exception if any + return; + } + + var timeout = RemainingTimeout; + if (timeout != System.Threading.Timeout.InfiniteTimeSpan && timeout < TimeSpan.Zero) + { + throw new TimeoutException(); + } + + try + { + if (!task.Wait((int)timeout.TotalMilliseconds, CancellationToken)) + { + CancellationToken.ThrowIfCancellationRequested(); + throw new TimeoutException(); + } + } + catch (AggregateException e) + { + if (e.InnerExceptions.Count == 1) + { + throw e.InnerExceptions[0]; + } + + throw; + } + } + + public async Task WaitTaskAsync(Task task) + { + if (task.IsCompleted) + { + await task.ConfigureAwait(false); // re-throws exception if any + return; + } + + var timeout = RemainingTimeout; + if (timeout != System.Threading.Timeout.InfiniteTimeSpan && timeout < TimeSpan.Zero) + { + throw new TimeoutException(); + } + + try + { + await task.WaitAsync(timeout, CancellationToken).ConfigureAwait(false); + } + catch (TaskCanceledException) + { + CancellationToken.ThrowIfCancellationRequested(); + throw; + } + } + + public OperationContext WithTimeout(TimeSpan timeout) + { + var remainingTimeout = RemainingTimeout; + if (timeout == System.Threading.Timeout.InfiniteTimeSpan) + { + timeout = remainingTimeout; + } + else if (remainingTimeout != System.Threading.Timeout.InfiniteTimeSpan && remainingTimeout < timeout) + { + timeout = remainingTimeout; + } + + return new OperationContext(timeout, CancellationToken) + { + ParentContext = this + }; + } + } +} + diff --git a/src/MongoDB.Driver/OperationExecutor.cs b/src/MongoDB.Driver/OperationExecutor.cs index 295d136bd09..7025097de70 100644 --- a/src/MongoDB.Driver/OperationExecutor.cs +++ b/src/MongoDB.Driver/OperationExecutor.cs @@ -50,9 +50,10 @@ public TResult ExecuteReadOperation( Ensure.IsNotNull(session, nameof(session)); ThrowIfDisposed(); + var operationContext = options.ToOperationContext(cancellationToken); var readPreference = options.GetEffectiveReadPreference(session); using var binding = CreateReadBinding(session, readPreference, allowChannelPinning); - return operation.Execute(binding, cancellationToken); + return operation.Execute(operationContext, binding); } public async Task ExecuteReadOperationAsync( @@ -67,9 +68,10 @@ public async Task ExecuteReadOperationAsync( Ensure.IsNotNull(session, nameof(session)); ThrowIfDisposed(); + var operationContext = options.ToOperationContext(cancellationToken); var readPreference = options.GetEffectiveReadPreference(session); using var binding = CreateReadBinding(session, readPreference, allowChannelPinning); - return await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); } public TResult ExecuteWriteOperation( @@ -84,8 +86,9 @@ public TResult ExecuteWriteOperation( Ensure.IsNotNull(session, nameof(session)); ThrowIfDisposed(); + var operationContext = options.ToOperationContext(cancellationToken); using var binding = CreateReadWriteBinding(session, allowChannelPinning); - return operation.Execute(binding, cancellationToken); + return operation.Execute(operationContext, binding); } public async Task ExecuteWriteOperationAsync( @@ -100,8 +103,9 @@ public async Task ExecuteWriteOperationAsync( Ensure.IsNotNull(session, nameof(session)); ThrowIfDisposed(); + var operationContext = options.ToOperationContext(cancellationToken); using var binding = CreateReadWriteBinding(session, allowChannelPinning); - return await operation.ExecuteAsync(binding, cancellationToken).ConfigureAwait(false); + return await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); } public IClientSessionHandle StartImplicitSession() diff --git a/src/MongoDB.Driver/OperationOptionsBase.cs b/src/MongoDB.Driver/OperationOptionsBase.cs new file mode 100644 index 00000000000..5f564ca1b27 --- /dev/null +++ b/src/MongoDB.Driver/OperationOptionsBase.cs @@ -0,0 +1,27 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; + +namespace MongoDB.Driver +{ + internal abstract record OperationOptionsBase(TimeSpan Timeout) + { + public OperationContext ToOperationContext(CancellationToken cancellationToken) + => new (Timeout, cancellationToken); + } +} + diff --git a/src/MongoDB.Driver/ReadOperationOptions.cs b/src/MongoDB.Driver/ReadOperationOptions.cs index 2473d5dc045..baba0248e8f 100644 --- a/src/MongoDB.Driver/ReadOperationOptions.cs +++ b/src/MongoDB.Driver/ReadOperationOptions.cs @@ -13,9 +13,12 @@ * limitations under the License. */ +using System; + namespace MongoDB.Driver { - internal record ReadOperationOptions(ReadPreference ExplicitReadPreference = null, ReadPreference DefaultReadPreference = null); + internal record ReadOperationOptions(TimeSpan Timeout, ReadPreference ExplicitReadPreference = null, ReadPreference DefaultReadPreference = null) + : OperationOptionsBase(Timeout); internal static class ReadOperationOptionsExtensions { diff --git a/src/MongoDB.Driver/WriteOperationOptions.cs b/src/MongoDB.Driver/WriteOperationOptions.cs index fd56dc0fa78..f141c2ebc39 100644 --- a/src/MongoDB.Driver/WriteOperationOptions.cs +++ b/src/MongoDB.Driver/WriteOperationOptions.cs @@ -13,8 +13,11 @@ * limitations under the License. */ +using System; + namespace MongoDB.Driver { - internal record WriteOperationOptions(); + internal record WriteOperationOptions(TimeSpan Timeout) + : OperationOptionsBase(Timeout); } diff --git a/tests/MongoDB.Driver.TestHelpers/Core/CoreTestConfiguration.cs b/tests/MongoDB.Driver.TestHelpers/Core/CoreTestConfiguration.cs index 4a50ba002d5..aae52d93b19 100644 --- a/tests/MongoDB.Driver.TestHelpers/Core/CoreTestConfiguration.cs +++ b/tests/MongoDB.Driver.TestHelpers/Core/CoreTestConfiguration.cs @@ -314,7 +314,7 @@ private static int GetMaxWireVersion() { var command = new BsonDocument("hello", 1); var operation = new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, __messageEncoderSettings); - var response = operation.Execute(binding, CancellationToken.None); + var response = operation.Execute(OperationContext.NoTimeout, binding); return response["maxWireVersion"].AsInt32; } } @@ -326,7 +326,7 @@ private static SemanticVersion GetServerVersion() { var command = new BsonDocument("buildinfo", 1); var operation = new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, __messageEncoderSettings); - var response = operation.Execute(binding, CancellationToken.None); + var response = operation.Execute(OperationContext.NoTimeout, binding); return SemanticVersion.Parse(response["version"].AsString); } } @@ -338,7 +338,7 @@ public static BsonDocument GetServerParameters() { var command = new BsonDocument("getParameter", new BsonString("*")); var operation = new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, __messageEncoderSettings); - var serverParameters = operation.Execute(binding, CancellationToken.None); + var serverParameters = operation.Execute(OperationContext.NoTimeout, binding); return serverParameters; } @@ -404,7 +404,7 @@ private static void DropDatabase() using (var session = StartSession()) using (var binding = CreateReadWriteBinding(session)) { - operation.Execute(binding, CancellationToken.None); + operation.Execute(OperationContext.NoTimeout, binding); } } @@ -415,7 +415,7 @@ private static IEnumerable FindDocuments(IClusterInternal cluster, { var operation = new FindOperation(collectionNamespace, BsonDocumentSerializer.Instance, __messageEncoderSettings); - return operation.Execute(binding, CancellationToken.None).ToList(); + return operation.Execute(OperationContext.NoTimeout, binding).ToList(); } } @@ -495,7 +495,7 @@ string GetStorageEngineForCluster(IClusterInternal cluster) { var operation = new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, __messageEncoderSettings); - var response = operation.Execute(binding, CancellationToken.None); + var response = operation.Execute(OperationContext.NoTimeout, binding); if (response.TryGetValue("storageEngine", out var storageEngine) && storageEngine.AsBsonDocument.TryGetValue("name", out var name)) { return name.AsString; diff --git a/tests/MongoDB.Driver.TestHelpers/Core/FailPoint.cs b/tests/MongoDB.Driver.TestHelpers/Core/FailPoint.cs index c9f6c9f9a4e..0122eb8447b 100644 --- a/tests/MongoDB.Driver.TestHelpers/Core/FailPoint.cs +++ b/tests/MongoDB.Driver.TestHelpers/Core/FailPoint.cs @@ -88,7 +88,7 @@ public static FailPoint ConfigureAlwaysOn(IClusterInternal cluster, ICoreSession private static IServer GetWriteableServer(IClusterInternal cluster) { var selector = WritableServerSelector.Instance; - return cluster.SelectServer(selector, CancellationToken.None); + return cluster.SelectServer(OperationContext.NoTimeout, selector); } private static void MakeFailPointApplicationNameTestableIfConfigured(BsonDocument command, bool async) @@ -186,7 +186,7 @@ private void ExecuteCommand(BsonDocument command, bool waitForConnected) BsonDocumentSerializer.Instance, new MessageEncoderSettings()); - operation.Execute(_binding, CancellationToken.None); + operation.Execute(OperationContext.NoTimeout, _binding); } } } diff --git a/tests/MongoDB.Driver.TestHelpers/Core/MockClusterableServerFactory.cs b/tests/MongoDB.Driver.TestHelpers/Core/MockClusterableServerFactory.cs index d865da3f833..9bc9a17f6a9 100644 --- a/tests/MongoDB.Driver.TestHelpers/Core/MockClusterableServerFactory.cs +++ b/tests/MongoDB.Driver.TestHelpers/Core/MockClusterableServerFactory.cs @@ -93,11 +93,11 @@ public IClusterableServer CreateServer(ClusterType clusterType, ClusterId cluste mockConnectionPool.Setup(p => p.Generation).Returns(valueFunction: () => poolGeneration); Action acquireConnectionCallback = () => { connectionGeneration = poolGeneration; }; mockConnectionPool - .Setup(p => p.AcquireConnection(It.IsAny())) + .Setup(p => p.AcquireConnection(It.IsAny())) .Callback(acquireConnectionCallback) .Returns(mockConnection.Object); mockConnectionPool - .Setup(p => p.AcquireConnectionAsync(It.IsAny())) + .Setup(p => p.AcquireConnectionAsync(It.IsAny())) .Callback(acquireConnectionCallback) .ReturnsAsync(mockConnection.Object); mockConnectionPool.Setup(p => p.Clear(It.IsAny())).Callback(() => { ++poolGeneration; }); @@ -186,7 +186,7 @@ public void PublishDescription(ServerDescription description) var maxWireVersion = description.MaxWireVersion; var server = (Server)result.Server; var helloResult = new HelloResult(new BsonDocument { { "compressors", new BsonArray() }, { "maxWireVersion", maxWireVersion } }); - var mockConnection = Mock.Get(server._connectionPool().AcquireConnection(CancellationToken.None)); + var mockConnection = Mock.Get(server._connectionPool().AcquireConnection(OperationContext.NoTimeout)); mockConnection.SetupGet(c => c.Description) .Returns(new ConnectionDescription(new ConnectionId(description.ServerId, 0), helloResult)); } diff --git a/tests/MongoDB.Driver.TestHelpers/DriverTestConfiguration.cs b/tests/MongoDB.Driver.TestHelpers/DriverTestConfiguration.cs index a0d0a2a8a4c..4a266dbb70a 100644 --- a/tests/MongoDB.Driver.TestHelpers/DriverTestConfiguration.cs +++ b/tests/MongoDB.Driver.TestHelpers/DriverTestConfiguration.cs @@ -197,8 +197,8 @@ public static ConnectionDescription GetConnectionDescription() { var cluster = Client.GetClusterInternal(); using (var binding = new ReadWriteBindingHandle(new WritableServerBinding(cluster, NoCoreSession.NewHandle()))) - using (var channelSource = binding.GetWriteChannelSource(default)) - using (var channel = channelSource.GetChannel(default)) + using (var channelSource = binding.GetWriteChannelSource(OperationContext.NoTimeout)) + using (var channel = channelSource.GetChannel(OperationContext.NoTimeout)) { return channel.ConnectionDescription; } diff --git a/tests/MongoDB.Driver.Tests/AuthenticationTests.cs b/tests/MongoDB.Driver.Tests/AuthenticationTests.cs index f60b30c80c5..0ad423e16eb 100644 --- a/tests/MongoDB.Driver.Tests/AuthenticationTests.cs +++ b/tests/MongoDB.Driver.Tests/AuthenticationTests.cs @@ -337,10 +337,9 @@ private void AssertAuthenticationSucceeds( if (Feature.SpeculativeAuthentication.IsSupported(CoreTestConfiguration.MaxWireVersion) && speculativeAuthenticatationShouldSucceedIfPossible) { - var cancellationToken = CancellationToken.None; var serverSelector = new ReadPreferenceServerSelector(settings.ReadPreference); - var server = client.GetClusterInternal().SelectServer(serverSelector, cancellationToken); - var channel = server.GetChannel(cancellationToken); + var server = client.GetClusterInternal().SelectServer(OperationContext.NoTimeout, serverSelector); + var channel = server.GetChannel(OperationContext.NoTimeout); var helloResult = channel.ConnectionDescription.HelloResult; helloResult.SpeculativeAuthenticate.Should().NotBeNull(); } diff --git a/tests/MongoDB.Driver.Tests/ClusterTests.cs b/tests/MongoDB.Driver.Tests/ClusterTests.cs index 1852264edc3..e1a453acefa 100644 --- a/tests/MongoDB.Driver.Tests/ClusterTests.cs +++ b/tests/MongoDB.Driver.Tests/ClusterTests.cs @@ -87,8 +87,8 @@ public void SelectServer_loadbalancing_prose_test([Values(false, true)] bool asy var eventCapturer = CreateEventCapturer(); using (var client = CreateMongoClient(eventCapturer, applicationName)) { - var slowServer = client.GetClusterInternal().SelectServer(WritableServerSelector.Instance, default); - var fastServer = client.GetClusterInternal().SelectServer(new DelegateServerSelector((_, servers) => servers.Where(s => s.ServerId != slowServer.ServerId)), default); + var slowServer = client.GetClusterInternal().SelectServer(OperationContext.NoTimeout, WritableServerSelector.Instance); + var fastServer = client.GetClusterInternal().SelectServer(OperationContext.NoTimeout, new DelegateServerSelector((_, servers) => servers.Where(s => s.ServerId != slowServer.ServerId))); using var failPoint = FailPoint.Configure(slowServer, NoCoreSession.NewHandle(), failCommand, async); @@ -100,8 +100,8 @@ public void SelectServer_loadbalancing_prose_test([Values(false, true)] bool asy var channels = new ConcurrentBag(); ThreadingUtilities.ExecuteOnNewThreads(threadsCount, i => { - channels.Add(slowServer.GetChannel(default)); - channels.Add(fastServer.GetChannel(default)); + channels.Add(slowServer.GetChannel(OperationContext.NoTimeout)); + channels.Add(fastServer.GetChannel(OperationContext.NoTimeout)); }); foreach (var channel in channels) diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelChannelSourceTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelChannelSourceTests.cs index 76a12582048..650f074169d 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelChannelSourceTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelChannelSourceTests.cs @@ -15,7 +15,7 @@ using System; using System.Reflection; -using System.Threading; +using System.Threading.Tasks; using FluentAssertions; using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Servers; @@ -140,25 +140,17 @@ public void Dispose_can_be_called_more_than_once() [Theory] [ParameterAttributeData] - public void GetChannel_should_return_expected_result( + public async Task GetChannel_should_return_expected_result( [Values(false, true)] bool async) { var mockChannel = new Mock(); var subject = CreateSubject(channel: mockChannel.Object); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; var expectedResult = new Mock().Object; mockChannel.Setup(m => m.Fork()).Returns(expectedResult); - IChannelHandle result; - if (async) - { - result = subject.GetChannelAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - result = subject.GetChannel(cancellationToken); - } + var result = async ? + await subject.GetChannelAsync(OperationContext.NoTimeout) : + subject.GetChannel(OperationContext.NoTimeout); result.Should().BeSameAs(expectedResult); mockChannel.Verify(m => m.Fork(), Times.Once); @@ -166,24 +158,14 @@ public void GetChannel_should_return_expected_result( [Theory] [ParameterAttributeData] - public void GetChannel_should_throw_when_disposed( + public async Task GetChannel_should_throw_when_disposed( [Values(false, true)] bool async) { var subject = CreateDisposedSubject(); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - - var exception = Record.Exception(() => - { - if (async) - { - subject.GetChannelAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - subject.GetChannel(cancellationToken); - } - }); + + var exception = async ? + await Record.ExceptionAsync(() => subject.GetChannelAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetChannel(OperationContext.NoTimeout)); var e = exception.Should().BeOfType().Subject; e.ObjectName.Should().Be(subject.GetType().FullName); diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelReadBindingTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelReadBindingTests.cs index 121011542cb..1d6f317e611 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelReadBindingTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelReadBindingTests.cs @@ -14,11 +14,7 @@ */ using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; -using System.Text; -using System.Threading; using System.Threading.Tasks; using FluentAssertions; using MongoDB.TestHelpers.XunitExtensions; @@ -151,29 +147,21 @@ public void Dispose_can_be_called_more_than_once() [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_return_expected_result( + public async Task GetReadChannelSource_should_return_expected_result( [Values(false, true)] bool async) { var mockChannel = new Mock(); var mockSession = new Mock(); var subject = CreateSubject(channel: mockChannel.Object, session: mockSession.Object); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; var forkedChannel = new Mock().Object; var forkedSession = new Mock().Object; mockChannel.Setup(m => m.Fork()).Returns(forkedChannel); mockSession.Setup(m => m.Fork()).Returns(forkedSession); - IChannelSourceHandle result; - if (async) - { - result = subject.GetReadChannelSourceAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - result = subject.GetReadChannelSource(cancellationToken); - } + var result = async ? + await subject.GetReadChannelSourceAsync(OperationContext.NoTimeout) : + subject.GetReadChannelSource(OperationContext.NoTimeout); var newHandle = result.Should().BeOfType().Subject; var referenceCounted = newHandle._reference(); @@ -184,24 +172,13 @@ public void GetReadChannelSource_should_return_expected_result( [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_throw_when_disposed( + public async Task GetReadChannelSource_should_throw_when_disposed( [Values(false, true)] bool async) { var subject = CreateDisposedSubject(); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - - var exception = Record.Exception(() => - { - if (async) - { - subject.GetReadChannelSourceAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - subject.GetReadChannelSource(cancellationToken); - } - }); + var exception = async ? + await Record.ExceptionAsync(() => subject.GetReadChannelSourceAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetReadChannelSource(OperationContext.NoTimeout)); var e = exception.Should().BeOfType().Subject; e.ObjectName.Should().Be(subject.GetType().FullName); diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelReadWriteBindingTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelReadWriteBindingTests.cs index 15c900f87d3..acc8fdfa4ad 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelReadWriteBindingTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelReadWriteBindingTests.cs @@ -14,11 +14,7 @@ */ using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; -using System.Text; -using System.Threading; using System.Threading.Tasks; using FluentAssertions; using MongoDB.TestHelpers.XunitExtensions; @@ -132,29 +128,21 @@ public void Dispose_can_be_called_more_than_once() [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_return_expected_result( + public async Task GetReadChannelSource_should_return_expected_result( [Values(false, true)] bool async) { var mockChannel = new Mock(); var mockSession = new Mock(); var subject = CreateSubject(channel: mockChannel.Object, session: mockSession.Object); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; var forkedChannel = new Mock().Object; var forkedSession = new Mock().Object; mockChannel.Setup(m => m.Fork()).Returns(forkedChannel); mockSession.Setup(m => m.Fork()).Returns(forkedSession); - IChannelSourceHandle result; - if (async) - { - result = subject.GetReadChannelSourceAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - result = subject.GetReadChannelSource(cancellationToken); - } + var result = async ? + await subject.GetReadChannelSourceAsync(OperationContext.NoTimeout) : + subject.GetReadChannelSource(OperationContext.NoTimeout); var newHandle = result.Should().BeOfType().Subject; var referenceCounted = newHandle._reference(); @@ -165,29 +153,21 @@ public void GetReadChannelSource_should_return_expected_result( [Theory] [ParameterAttributeData] - public void GetWriteChannelSource_should_return_expected_result( + public async Task GetWriteChannelSource_should_return_expected_result( [Values(false, true)] bool async) { var mockChannel = new Mock(); var mockSession = new Mock(); var subject = CreateSubject(channel: mockChannel.Object, session: mockSession.Object); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; var forkedChannel = new Mock().Object; var forkedSession = new Mock().Object; mockChannel.Setup(m => m.Fork()).Returns(forkedChannel); mockSession.Setup(m => m.Fork()).Returns(forkedSession); - IChannelSourceHandle result; - if (async) - { - result = subject.GetWriteChannelSourceAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - result = subject.GetWriteChannelSource(cancellationToken); - } + var result = async ? + await subject.GetWriteChannelSourceAsync(OperationContext.NoTimeout) : + subject.GetWriteChannelSource(OperationContext.NoTimeout); var newHandle = result.Should().BeOfType().Subject; var referenceCounted = newHandle._reference(); @@ -198,24 +178,13 @@ public void GetWriteChannelSource_should_return_expected_result( [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_throw_when_disposed( + public async Task GetReadChannelSource_should_throw_when_disposed( [Values(false, true)] bool async) { var subject = CreateDisposedSubject(); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - - var exception = Record.Exception(() => - { - if (async) - { - subject.GetReadChannelSourceAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - subject.GetReadChannelSource(cancellationToken); - } - }); + var exception = async ? + await Record.ExceptionAsync(() => subject.GetReadChannelSourceAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetReadChannelSource(OperationContext.NoTimeout)); var e = exception.Should().BeOfType().Subject; e.ObjectName.Should().Be(subject.GetType().FullName); @@ -223,24 +192,13 @@ public void GetReadChannelSource_should_throw_when_disposed( [Theory] [ParameterAttributeData] - public void GetWriteChannelSource_should_throw_when_disposed( + public async Task GetWriteChannelSource_should_throw_when_disposed( [Values(false, true)] bool async) { var subject = CreateDisposedSubject(); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - - var exception = Record.Exception(() => - { - if (async) - { - subject.GetWriteChannelSourceAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - subject.GetWriteChannelSource(cancellationToken); - } - }); + var exception = async ? + await Record.ExceptionAsync(() => subject.GetWriteChannelSourceAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetWriteChannelSource(OperationContext.NoTimeout)); var e = exception.Should().BeOfType().Subject; e.ObjectName.Should().Be(subject.GetType().FullName); diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelSourceHandleTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelSourceHandleTests.cs index 212ca5f46d9..37031583a76 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelSourceHandleTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelSourceHandleTests.cs @@ -15,6 +15,7 @@ using System; using System.Threading; +using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson.TestHelpers; using MongoDB.TestHelpers.XunitExtensions; @@ -53,29 +54,23 @@ public void Session_should_delegate_to_reference() [Theory] [ParameterAttributeData] - public void GetChannel_should_throw_if_disposed( + public async Task GetChannel_should_throw_if_disposed( [Values(false, true)] bool async) { var subject = new ChannelSourceHandle(_mockChannelSource.Object); subject.Dispose(); - Action act; - if (async) - { - act = () => subject.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.GetChannel(CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.GetChannelAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetChannel(OperationContext.NoTimeout)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void GetChannel_should_delegate_to_reference( + public async Task GetChannel_should_delegate_to_reference( [Values(false, true)] bool async) { @@ -83,15 +78,15 @@ public void GetChannel_should_delegate_to_reference( if (async) { - subject.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult(); + await subject.GetChannelAsync(OperationContext.NoTimeout); - _mockChannelSource.Verify(s => s.GetChannelAsync(CancellationToken.None), Times.Once); + _mockChannelSource.Verify(s => s.GetChannelAsync(It.IsAny()), Times.Once); } else { - subject.GetChannel(CancellationToken.None); + subject.GetChannel(OperationContext.NoTimeout); - _mockChannelSource.Verify(s => s.GetChannel(CancellationToken.None), Times.Once); + _mockChannelSource.Verify(s => s.GetChannel(It.IsAny()), Times.Once); } } diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelSourceReadWriteBindingTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelSourceReadWriteBindingTests.cs index d00f0151e47..fbac6fc6c7b 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelSourceReadWriteBindingTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/ChannelSourceReadWriteBindingTests.cs @@ -14,11 +14,9 @@ */ using System; -using System.Threading; +using System.Threading.Tasks; using FluentAssertions; using MongoDB.TestHelpers.XunitExtensions; -using MongoDB.Driver.Core.Bindings; -using MongoDB.Driver.Core.Clusters; using Moq; using Xunit; @@ -78,84 +76,60 @@ public void Session_should_return_expected_result() [Theory] [ParameterAttributeData] - public void GetReadChannelSourceAsync_should_throw_if_disposed( + public async Task GetReadChannelSourceAsync_should_throw_if_disposed( [Values(false, true)] bool async) { var subject = new ChannelSourceReadWriteBinding(_mockChannelSource.Object, ReadPreference.Primary, NoCoreSession.NewHandle()); subject.Dispose(); - Action act; - if (async) - { - act = () => subject.GetReadChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.GetReadChannelSource(CancellationToken.None); - } - - act.ShouldThrow(); + var exception = async ? + await Record.ExceptionAsync(() => subject.GetReadChannelSourceAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetReadChannelSource(OperationContext.NoTimeout)); + + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_fork_the_channelSource( + public async Task GetReadChannelSource_should_fork_the_channelSource( [Values(false, true)] bool async) { var subject = new ChannelSourceReadWriteBinding(_mockChannelSource.Object, ReadPreference.Primary, NoCoreSession.NewHandle()); - - if (async) - { - subject.GetReadChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - subject.GetReadChannelSource(CancellationToken.None); - } + var result = async ? + await subject.GetReadChannelSourceAsync(OperationContext.NoTimeout) : + subject.GetReadChannelSource(OperationContext.NoTimeout); _mockChannelSource.Verify(f => f.Fork(), Times.Once); } [Theory] [ParameterAttributeData] - public void GetWriteChannelSource_should_throw_if_disposed( + public async Task GetWriteChannelSource_should_throw_if_disposed( [Values(false, true)] bool async) { var subject = new ChannelSourceReadWriteBinding(_mockChannelSource.Object, ReadPreference.Primary, NoCoreSession.NewHandle()); subject.Dispose(); - Action act; - if (async) - { - act = () => subject.GetWriteChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.GetWriteChannelSource(CancellationToken.None); - } - - act.ShouldThrow(); + var exception = async ? + await Record.ExceptionAsync(() => subject.GetWriteChannelSourceAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetWriteChannelSource(OperationContext.NoTimeout)); + + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void GetWriteChannelSource_should_fork_the_channelSource( + public async Task GetWriteChannelSource_should_fork_the_channelSource( [Values(false, true)] bool async) { var subject = new ChannelSourceReadWriteBinding(_mockChannelSource.Object, ReadPreference.Primary, NoCoreSession.NewHandle()); - - if (async) - { - subject.GetWriteChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - subject.GetWriteChannelSource(CancellationToken.None); - } + var result = async ? + await subject.GetWriteChannelSourceAsync(OperationContext.NoTimeout) : + subject.GetWriteChannelSource(OperationContext.NoTimeout); _mockChannelSource.Verify(f => f.Fork(), Times.Once); } diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/ReadBindingHandleTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/ReadBindingHandleTests.cs index d26b05ff749..070b01d7e48 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/ReadBindingHandleTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/ReadBindingHandleTests.cs @@ -14,7 +14,7 @@ */ using System; -using System.Threading; +using System.Threading.Tasks; using FluentAssertions; using MongoDB.TestHelpers.XunitExtensions; using Moq; @@ -51,29 +51,23 @@ public void Session_should_delegate_to_reference() [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_throw_if_disposed( + public async Task GetReadChannelSource_should_throw_if_disposed( [Values(false, true)] bool async) { var subject = new ReadBindingHandle(_mockReadBinding.Object); subject.Dispose(); - Action act; - if (async) - { - act = () => subject.GetReadChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.GetReadChannelSource(CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.GetReadChannelSourceAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetReadChannelSource(OperationContext.NoTimeout)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_delegate_to_reference( + public async Task GetReadChannelSource_should_delegate_to_reference( [Values(false, true)] bool async) { @@ -81,15 +75,15 @@ public void GetReadChannelSource_should_delegate_to_reference( if (async) { - subject.GetReadChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); + await subject.GetReadChannelSourceAsync(OperationContext.NoTimeout); - _mockReadBinding.Verify(b => b.GetReadChannelSourceAsync(CancellationToken.None), Times.Once); + _mockReadBinding.Verify(b => b.GetReadChannelSourceAsync(OperationContext.NoTimeout), Times.Once); } else { - subject.GetReadChannelSource(CancellationToken.None); + subject.GetReadChannelSource(OperationContext.NoTimeout); - _mockReadBinding.Verify(b => b.GetReadChannelSource(CancellationToken.None), Times.Once); + _mockReadBinding.Verify(b => b.GetReadChannelSource(OperationContext.NoTimeout), Times.Once); } } diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/ReadPreferenceBindingTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/ReadPreferenceBindingTests.cs index 744241191af..c3486f158e5 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/ReadPreferenceBindingTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/ReadPreferenceBindingTests.cs @@ -16,7 +16,6 @@ using System; using System.Net; using System.Reflection; -using System.Threading; using System.Threading.Tasks; using FluentAssertions; using MongoDB.Driver.Core.Clusters; @@ -75,29 +74,23 @@ public void Session_should_return_expected_result() [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_throw_if_disposed( + public async Task GetReadChannelSource_should_throw_if_disposed( [Values(false, true)] bool async) { var subject = new ReadPreferenceBinding(_mockCluster.Object, ReadPreference.Primary, NoCoreSession.NewHandle()); subject.Dispose(); - Action act; - if (async) - { - act = () => subject.GetReadChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.GetReadChannelSource(CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.GetReadChannelSourceAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetReadChannelSource(OperationContext.NoTimeout)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_use_a_read_preference_server_selector_to_select_the_server_from_the_cluster( + public async Task GetReadChannelSource_should_use_a_read_preference_server_selector_to_select_the_server_from_the_cluster( [Values(false, true)] bool async) { @@ -118,35 +111,32 @@ public void GetReadChannelSource_should_use_a_read_preference_server_selector_to if (async) { - _mockCluster.Setup(c => c.SelectServerAsync(It.IsAny(), CancellationToken.None)).Returns(Task.FromResult(selectedServer)); + _mockCluster.Setup(c => c.SelectServerAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(selectedServer)); - subject.GetReadChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); + await subject.GetReadChannelSourceAsync(OperationContext.NoTimeout); - _mockCluster.Verify(c => c.SelectServerAsync(It.IsAny(), CancellationToken.None), Times.Once); + _mockCluster.Verify(c => c.SelectServerAsync(It.IsAny(), It.IsAny()), Times.Once); } else { - _mockCluster.Setup(c => c.SelectServer(It.IsAny(), CancellationToken.None)).Returns(selectedServer); + _mockCluster.Setup(c => c.SelectServer(It.IsAny(), It.IsAny())).Returns(selectedServer); - subject.GetReadChannelSource(CancellationToken.None); + subject.GetReadChannelSource(OperationContext.NoTimeout); - _mockCluster.Verify(c => c.SelectServer(It.IsAny(), CancellationToken.None), Times.Once); + _mockCluster.Verify(c => c.SelectServer(It.IsAny(), It.IsAny()), Times.Once); } } [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_fork_the_session( + public async Task GetReadChannelSource_should_fork_the_session( [Values(false, true)] bool async) { var mockSession = new Mock(); var subject = new ReadPreferenceBinding(_mockCluster.Object, ReadPreference.Primary, mockSession.Object); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - var selectedServer = new Mock().Object; - _mockCluster.Setup(m => m.SelectServer(It.IsAny(), cancellationToken)).Returns(selectedServer); - _mockCluster.Setup(m => m.SelectServerAsync(It.IsAny(), cancellationToken)).Returns(Task.FromResult(selectedServer)); + _mockCluster.Setup(m => m.SelectServer(It.IsAny(), It.IsAny())).Returns(selectedServer); + _mockCluster.Setup(m => m.SelectServerAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(selectedServer)); var forkedSession = new Mock().Object; mockSession.Setup(m => m.Fork()).Returns(forkedSession); @@ -162,15 +152,9 @@ public void GetReadChannelSource_should_fork_the_session( var finalClusterDescription = initialClusterDescription.WithType(ClusterType.Standalone); _mockCluster.SetupSequence(c => c.Description).Returns(initialClusterDescription).Returns(finalClusterDescription); - IChannelSourceHandle result; - if (async) - { - result = subject.GetReadChannelSourceAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - result = subject.GetReadChannelSource(cancellationToken); - } + var result = async ? + await subject.GetReadChannelSourceAsync(OperationContext.NoTimeout) : + subject.GetReadChannelSource(OperationContext.NoTimeout); var handle = result.Should().BeOfType().Subject; var referenceCounted = handle._reference().Should().BeOfType>().Subject; diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/ReadWriteBindingHandleTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/ReadWriteBindingHandleTests.cs index 33f36397c1b..7549be8c4c8 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/ReadWriteBindingHandleTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/ReadWriteBindingHandleTests.cs @@ -14,10 +14,9 @@ */ using System; -using System.Threading; +using System.Threading.Tasks; using FluentAssertions; using MongoDB.TestHelpers.XunitExtensions; -using MongoDB.Driver.Core.Bindings; using Moq; using Xunit; @@ -52,29 +51,23 @@ public void Session_should_delegate_to_reference() [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_throw_if_disposed( + public async Task GetReadChannelSource_should_throw_if_disposed( [Values(false, true)] bool async) { var subject = new ReadWriteBindingHandle(_mockReadWriteBinding.Object); subject.Dispose(); - Action act; - if (async) - { - act = () => subject.GetReadChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.GetReadChannelSource(CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.GetReadChannelSourceAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetReadChannelSource(OperationContext.NoTimeout)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_delegate_to_reference( + public async Task GetReadChannelSource_should_delegate_to_reference( [Values(false, true)] bool async) { @@ -82,43 +75,37 @@ public void GetReadChannelSource_should_delegate_to_reference( if (async) { - subject.GetReadChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); + await subject.GetReadChannelSourceAsync(OperationContext.NoTimeout); - _mockReadWriteBinding.Verify(b => b.GetReadChannelSourceAsync(CancellationToken.None), Times.Once); + _mockReadWriteBinding.Verify(b => b.GetReadChannelSourceAsync(It.IsAny()), Times.Once); } else { - subject.GetReadChannelSource(CancellationToken.None); + subject.GetReadChannelSource(OperationContext.NoTimeout); - _mockReadWriteBinding.Verify(b => b.GetReadChannelSource(CancellationToken.None), Times.Once); + _mockReadWriteBinding.Verify(b => b.GetReadChannelSource(It.IsAny()), Times.Once); } } [Theory] [ParameterAttributeData] - public void GetWriteChannelSource_should_throw_if_disposed( + public async Task GetWriteChannelSource_should_throw_if_disposed( [Values(false, true)] bool async) { var subject = new ReadWriteBindingHandle(_mockReadWriteBinding.Object); subject.Dispose(); - Action act; - if (async) - { - act = () => subject.GetWriteChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.GetWriteChannelSource(CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.GetWriteChannelSourceAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetWriteChannelSource(OperationContext.NoTimeout)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void GetWriteChannelSource_should_delegate_to_reference( + public async Task GetWriteChannelSource_should_delegate_to_reference( [Values(false, true)] bool async) { @@ -126,15 +113,15 @@ public void GetWriteChannelSource_should_delegate_to_reference( if (async) { - subject.GetWriteChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); + await subject.GetWriteChannelSourceAsync(OperationContext.NoTimeout); - _mockReadWriteBinding.Verify(b => b.GetWriteChannelSourceAsync(CancellationToken.None), Times.Once); + _mockReadWriteBinding.Verify(b => b.GetWriteChannelSourceAsync(It.IsAny()), Times.Once); } else { - subject.GetWriteChannelSource(CancellationToken.None); + subject.GetWriteChannelSource(OperationContext.NoTimeout); - _mockReadWriteBinding.Verify(b => b.GetWriteChannelSource(CancellationToken.None), Times.Once); + _mockReadWriteBinding.Verify(b => b.GetWriteChannelSource(It.IsAny()), Times.Once); } } diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/ServerChannelSourceTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/ServerChannelSourceTests.cs index 9b20a631e7b..62bd5cfd535 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/ServerChannelSourceTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/ServerChannelSourceTests.cs @@ -14,13 +14,8 @@ */ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Servers; using MongoDB.Driver.Core.Helpers; @@ -43,17 +38,19 @@ public ServerChannelSourceTests() public void Constructor_should_throw_when_server_is_null() { var session = new Mock().Object; - Action act = () => new ServerChannelSource(null, session); + var exception = Record.Exception(() => new ServerChannelSource(null, session)); - act.ShouldThrow(); + exception.Should().BeOfType() + .Subject.ParamName.Should().Be("server"); } [Fact] public void Constructor_should_throw_when_session_is_null() { - Action act = () => new ServerChannelSource(_mockServer.Object, null); + var exception = Record.Exception(() => new ServerChannelSource(_mockServer.Object, null)); - act.ShouldThrow(); + exception.Should().BeOfType() + .Subject.ParamName.Should().Be("session"); } [Fact] @@ -84,7 +81,7 @@ public void Session_should_return_expected_result() [Theory] [ParameterAttributeData] - public void GetChannel_should_throw_if_disposed( + public async Task GetChannel_should_throw_if_disposed( [Values(false, true)] bool async) { @@ -92,22 +89,16 @@ public void GetChannel_should_throw_if_disposed( var subject = new ServerChannelSource(_mockServer.Object, session); subject.Dispose(); - Action act; - if (async) - { - act = () => subject.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.GetChannel(CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.GetChannelAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetChannel(OperationContext.NoTimeout)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void GetChannel_should_get_connection_from_server( + public async Task GetChannel_should_get_connection_from_server( [Values(false, true)] bool async) { @@ -116,15 +107,15 @@ public void GetChannel_should_get_connection_from_server( if (async) { - subject.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult(); + await subject.GetChannelAsync(OperationContext.NoTimeout); - _mockServer.Verify(s => s.GetChannelAsync(CancellationToken.None), Times.Once); + _mockServer.Verify(s => s.GetChannelAsync(It.IsAny()), Times.Once); } else { - subject.GetChannel(CancellationToken.None); + subject.GetChannel(OperationContext.NoTimeout); - _mockServer.Verify(s => s.GetChannel(CancellationToken.None), Times.Once); + _mockServer.Verify(s => s.GetChannel(It.IsAny()), Times.Once); } } diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/SingleServerReadBindingTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/SingleServerReadBindingTests.cs index 0c8e53a5321..ab0fae6858c 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/SingleServerReadBindingTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/SingleServerReadBindingTests.cs @@ -17,6 +17,7 @@ using System.Net; using System.Reflection; using System.Threading; +using System.Threading.Tasks; using FluentAssertions; using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Servers; @@ -126,26 +127,17 @@ public void Dispose_can_be_called_more_than_once() [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_return_expected_result( + public async Task GetReadChannelSource_should_return_expected_result( [Values(false, true)] bool async) { var mockSession = new Mock(); var subject = CreateSubject(session: mockSession.Object); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - var forkedSession = new Mock().Object; mockSession.Setup(m => m.Fork()).Returns(forkedSession); - IChannelSourceHandle result; - if (async) - { - result = subject.GetReadChannelSourceAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - result = subject.GetReadChannelSource(cancellationToken); - } + var result = async ? + await subject.GetReadChannelSourceAsync(OperationContext.NoTimeout) : + subject.GetReadChannelSource(OperationContext.NoTimeout); var newHandle = result.Should().BeOfType().Subject; var referenceCounted = newHandle._reference(); @@ -155,24 +147,14 @@ public void GetReadChannelSource_should_return_expected_result( [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_throw_when_disposed( + public async Task GetReadChannelSource_should_throw_when_disposed( [Values(false, true)] bool async) { var subject = CreateDisposedSubject(); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - - var exception = Record.Exception(() => - { - if (async) - { - subject.GetReadChannelSourceAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - subject.GetReadChannelSource(cancellationToken); - } - }); + + var exception = async ? + await Record.ExceptionAsync(() => subject.GetReadChannelSourceAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetReadChannelSource(OperationContext.NoTimeout)); var e = exception.Should().BeOfType().Subject; e.ObjectName.Should().Be(subject.GetType().FullName); diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/SingleServerReadWriteBindingTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/SingleServerReadWriteBindingTests.cs index 1087d544760..2efd25ca017 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/SingleServerReadWriteBindingTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/SingleServerReadWriteBindingTests.cs @@ -14,11 +14,7 @@ */ using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; -using System.Text; -using System.Threading; using System.Threading.Tasks; using FluentAssertions; using MongoDB.TestHelpers.XunitExtensions; @@ -112,26 +108,17 @@ public void Dispose_can_be_called_more_than_once() [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_return_expected_result( + public async Task GetReadChannelSource_should_return_expected_result( [Values(false, true)] bool async) { var mockSession = new Mock(); var subject = CreateSubject(session: mockSession.Object); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - var forkedSession = new Mock().Object; mockSession.Setup(m => m.Fork()).Returns(forkedSession); - IChannelSourceHandle result; - if (async) - { - result = subject.GetReadChannelSourceAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - result = subject.GetReadChannelSource(cancellationToken); - } + var result = async ? + await subject.GetReadChannelSourceAsync(OperationContext.NoTimeout) : + subject.GetReadChannelSource(OperationContext.NoTimeout); var newHandle = result.Should().BeOfType().Subject; var referenceCounted = newHandle._reference(); @@ -141,24 +128,13 @@ public void GetReadChannelSource_should_return_expected_result( [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_throw_when_disposed( + public async Task GetReadChannelSource_should_throw_when_disposed( [Values(false, true)] bool async) { var subject = CreateDisposedSubject(); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - - var exception = Record.Exception(() => - { - if (async) - { - subject.GetReadChannelSourceAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - subject.GetReadChannelSource(cancellationToken); - } - }); + var exception = async ? + await Record.ExceptionAsync(() => subject.GetReadChannelSourceAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetReadChannelSource(OperationContext.NoTimeout)); var e = exception.Should().BeOfType().Subject; e.ObjectName.Should().Be(subject.GetType().FullName); @@ -166,26 +142,17 @@ public void GetReadChannelSource_should_throw_when_disposed( [Theory] [ParameterAttributeData] - public void GetWriteChannelSource_should_return_expected_result( + public async Task GetWriteChannelSource_should_return_expected_result( [Values(false, true)] bool async) { var mockSession = new Mock(); var subject = CreateSubject(session: mockSession.Object); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - var forkedSession = new Mock().Object; mockSession.Setup(m => m.Fork()).Returns(forkedSession); - IChannelSourceHandle result; - if (async) - { - result = subject.GetWriteChannelSourceAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - result = subject.GetWriteChannelSource(cancellationToken); - } + var result = async ? + await subject.GetWriteChannelSourceAsync(OperationContext.NoTimeout) : + subject.GetWriteChannelSource(OperationContext.NoTimeout); var newHandle = result.Should().BeOfType().Subject; var referenceCounted = newHandle._reference(); @@ -195,24 +162,13 @@ public void GetWriteChannelSource_should_return_expected_result( [Theory] [ParameterAttributeData] - public void GetWriteChannelSource_should_throw_when_disposed( + public async Task GetWriteChannelSource_should_throw_when_disposed( [Values(false, true)] bool async) { var subject = CreateDisposedSubject(); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - - var exception = Record.Exception(() => - { - if (async) - { - subject.GetWriteChannelSourceAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - subject.GetWriteChannelSource(cancellationToken); - } - }); + var exception = async ? + await Record.ExceptionAsync(() => subject.GetReadChannelSourceAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetReadChannelSource(OperationContext.NoTimeout)); var e = exception.Should().BeOfType().Subject; e.ObjectName.Should().Be(subject.GetType().FullName); diff --git a/tests/MongoDB.Driver.Tests/Core/Bindings/WritableServerBindingTests.cs b/tests/MongoDB.Driver.Tests/Core/Bindings/WritableServerBindingTests.cs index a5796c5e5a0..f16dc755894 100644 --- a/tests/MongoDB.Driver.Tests/Core/Bindings/WritableServerBindingTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Bindings/WritableServerBindingTests.cs @@ -16,7 +16,6 @@ using System; using System.Net; using System.Reflection; -using System.Threading; using System.Threading.Tasks; using FluentAssertions; using MongoDB.Driver.Core.Clusters; @@ -77,29 +76,23 @@ public void Session_should_return_expected_result() [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_throw_if_disposed( + public async Task GetReadChannelSource_should_throw_if_disposed( [Values(false, true)] bool async) { var subject = new WritableServerBinding(_mockCluster.Object, NoCoreSession.NewHandle()); subject.Dispose(); - Action act; - if (async) - { - act = () => subject.GetReadChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.GetReadChannelSource(CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.GetReadChannelSourceAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetReadChannelSource(OperationContext.NoTimeout)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void GetReadChannelSource_should_use_a_writable_server_selector_to_select_the_server_from_the_cluster( + public async Task GetReadChannelSource_should_use_a_writable_server_selector_to_select_the_server_from_the_cluster( [Values(false, true)] bool async) { @@ -122,47 +115,41 @@ public void GetReadChannelSource_should_use_a_writable_server_selector_to_select if (async) { - _mockCluster.Setup(c => c.SelectServerAsync(It.IsAny(), CancellationToken.None)).Returns(Task.FromResult(selectedServer)); + _mockCluster.Setup(c => c.SelectServerAsync(OperationContext.NoTimeout, It.IsAny())).Returns(Task.FromResult(selectedServer)); - subject.GetReadChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); + await subject.GetReadChannelSourceAsync(OperationContext.NoTimeout); - _mockCluster.Verify(c => c.SelectServerAsync(It.IsAny(), CancellationToken.None), Times.Once); + _mockCluster.Verify(c => c.SelectServerAsync(OperationContext.NoTimeout, It.IsAny()), Times.Once); } else { - _mockCluster.Setup(c => c.SelectServer(It.IsAny(), CancellationToken.None)).Returns(selectedServer); + _mockCluster.Setup(c => c.SelectServer(OperationContext.NoTimeout, It.IsAny())).Returns(selectedServer); - subject.GetReadChannelSource(CancellationToken.None); + subject.GetReadChannelSource(OperationContext.NoTimeout); - _mockCluster.Verify(c => c.SelectServer(It.IsAny(), CancellationToken.None), Times.Once); + _mockCluster.Verify(c => c.SelectServer(OperationContext.NoTimeout, It.IsAny()), Times.Once); } } [Theory] [ParameterAttributeData] - public void GetWriteChannelSource_should_throw_if_disposed( + public async Task GetWriteChannelSource_should_throw_if_disposed( [Values(false, true)] bool async) { var subject = new WritableServerBinding(_mockCluster.Object, NoCoreSession.NewHandle()); subject.Dispose(); - Action act; - if (async) - { - act = () => subject.GetWriteChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.GetWriteChannelSource(CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.GetWriteChannelSourceAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetWriteChannelSource(OperationContext.NoTimeout)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void GetWriteChannelSourceAsync_should_use_a_writable_server_selector_to_select_the_server_from_the_cluster( + public async Task GetWriteChannelSourceAsync_should_use_a_writable_server_selector_to_select_the_server_from_the_cluster( [Values(false, true)] bool async) { @@ -184,19 +171,19 @@ public void GetWriteChannelSourceAsync_should_use_a_writable_server_selector_to_ if (async) { - _mockCluster.Setup(c => c.SelectServerAsync(It.IsAny(), CancellationToken.None)).Returns(Task.FromResult(selectedServer)); + _mockCluster.Setup(c => c.SelectServerAsync(OperationContext.NoTimeout, It.IsAny())).Returns(Task.FromResult(selectedServer)); - subject.GetWriteChannelSourceAsync(CancellationToken.None).GetAwaiter().GetResult(); + await subject.GetWriteChannelSourceAsync(OperationContext.NoTimeout); - _mockCluster.Verify(c => c.SelectServerAsync(It.IsAny(), CancellationToken.None), Times.Once); + _mockCluster.Verify(c => c.SelectServerAsync(OperationContext.NoTimeout, It.IsAny()), Times.Once); } else { - _mockCluster.Setup(c => c.SelectServer(It.IsAny(), CancellationToken.None)).Returns(selectedServer); + _mockCluster.Setup(c => c.SelectServer(OperationContext.NoTimeout, It.IsAny())).Returns(selectedServer); - subject.GetWriteChannelSource(CancellationToken.None); + subject.GetWriteChannelSource(OperationContext.NoTimeout); - _mockCluster.Verify(c => c.SelectServer(It.IsAny(), CancellationToken.None), Times.Once); + _mockCluster.Verify(c => c.SelectServer(OperationContext.NoTimeout, It.IsAny()), Times.Once); } } @@ -225,25 +212,25 @@ public async Task GetWriteChannelSource_should_use_a_composite_server_selector_t if (async) { - _mockCluster.Setup(c => c.SelectServerAsync(It.Is(cp => cp.ToString().Contains("PriorityServerSelector")), CancellationToken.None)).Returns(Task.FromResult(selectedServer)); + _mockCluster.Setup(c => c.SelectServerAsync(OperationContext.NoTimeout, It.Is(cp => cp.ToString().Contains("PriorityServerSelector")))).Returns(Task.FromResult(selectedServer)); - await subject.GetWriteChannelSourceAsync(deprioritizedServers, CancellationToken.None); + await subject.GetWriteChannelSourceAsync(OperationContext.NoTimeout, deprioritizedServers); - _mockCluster.Verify(c => c.SelectServerAsync(It.Is(cp => cp.ToString().Contains("PriorityServerSelector")), CancellationToken.None), Times.Once); + _mockCluster.Verify(c => c.SelectServerAsync(OperationContext.NoTimeout, It.Is(cp => cp.ToString().Contains("PriorityServerSelector"))), Times.Once); } else { - _mockCluster.Setup(c => c.SelectServer(It.Is(cp => cp.ToString().Contains("PriorityServerSelector")), CancellationToken.None)).Returns(selectedServer); + _mockCluster.Setup(c => c.SelectServer(OperationContext.NoTimeout, It.Is(cp => cp.ToString().Contains("PriorityServerSelector")))).Returns(selectedServer); - subject.GetWriteChannelSource(deprioritizedServers, CancellationToken.None); + subject.GetWriteChannelSource(OperationContext.NoTimeout, deprioritizedServers); - _mockCluster.Verify(c => c.SelectServer(It.Is(c => c.ToString().Contains("PriorityServerSelector")), CancellationToken.None), Times.Once); + _mockCluster.Verify(c => c.SelectServer(OperationContext.NoTimeout, It.Is(c => c.ToString().Contains("PriorityServerSelector"))), Times.Once); } } [Theory] [ParameterAttributeData] - public void GetWriteChannelSource_with_mayUseSecondary_should_pass_mayUseSecondary_to_server_selector( + public async Task GetWriteChannelSource_with_mayUseSecondary_should_pass_mayUseSecondary_to_server_selector( [Values(false, true)] bool async) { @@ -269,19 +256,19 @@ public void GetWriteChannelSource_with_mayUseSecondary_should_pass_mayUseSeconda if (async) { - _mockCluster.Setup(c => c.SelectServerAsync(It.IsAny(), CancellationToken.None)).Returns(Task.FromResult(selectedServer)); + _mockCluster.Setup(c => c.SelectServerAsync(OperationContext.NoTimeout, It.IsAny())).Returns(Task.FromResult(selectedServer)); - subject.GetWriteChannelSourceAsync(mayUseSecondary, CancellationToken.None).GetAwaiter().GetResult(); + await subject.GetWriteChannelSourceAsync(OperationContext.NoTimeout, mayUseSecondary); - _mockCluster.Verify(c => c.SelectServerAsync(It.Is(s => s.MayUseSecondary == mayUseSecondary), CancellationToken.None), Times.Once); + _mockCluster.Verify(c => c.SelectServerAsync(OperationContext.NoTimeout, It.Is(s => s.MayUseSecondary == mayUseSecondary)), Times.Once); } else { - _mockCluster.Setup(c => c.SelectServer(It.IsAny(), CancellationToken.None)).Returns(selectedServer); + _mockCluster.Setup(c => c.SelectServer(OperationContext.NoTimeout, It.IsAny())).Returns(selectedServer); - subject.GetWriteChannelSource(mayUseSecondary, CancellationToken.None); + subject.GetWriteChannelSource(OperationContext.NoTimeout, mayUseSecondary); - _mockCluster.Verify(c => c.SelectServer(It.Is(s => s.MayUseSecondary == mayUseSecondary), CancellationToken.None), Times.Once); + _mockCluster.Verify(c => c.SelectServer(OperationContext.NoTimeout, It.Is(s => s.MayUseSecondary == mayUseSecondary)), Times.Once); } } diff --git a/tests/MongoDB.Driver.Tests/Core/Clusters/ClusterTests.cs b/tests/MongoDB.Driver.Tests/Core/Clusters/ClusterTests.cs index 0d2b8bd2d20..7a3244b229d 100644 --- a/tests/MongoDB.Driver.Tests/Core/Clusters/ClusterTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Clusters/ClusterTests.cs @@ -120,29 +120,23 @@ public void AcquireServerSession_should_call_serverSessionPool_AcquireSession() [Theory] [ParameterAttributeData] - public void SelectServer_should_throw_if_not_initialized( + public async Task SelectServer_should_throw_if_not_initialized( [Values(false, true)] bool async) { var selector = new Mock().Object; var subject = CreateSubject(); - Action act; - if (async) - { - act = () => subject.SelectServerAsync(selector, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.SelectServer(selector, CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.SelectServerAsync(OperationContext.NoTimeout, selector)) : + Record.Exception(() => subject.SelectServer(OperationContext.NoTimeout, selector)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void SelectServer_should_throw_if_disposed( + public async Task SelectServer_should_throw_if_disposed( [Values(false, true)] bool async) { @@ -150,44 +144,32 @@ public void SelectServer_should_throw_if_disposed( var subject = CreateSubject(); subject.Dispose(); - Action act; - if (async) - { - act = () => subject.SelectServerAsync(selector, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.SelectServer(selector, CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.SelectServerAsync(OperationContext.NoTimeout, selector)) : + Record.Exception(() => subject.SelectServer(OperationContext.NoTimeout, selector)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void SelectServer_should_throw_if_serverSelector_is_null( + public async Task SelectServer_should_throw_if_serverSelector_is_null( [Values(false, true)] bool async) { var subject = CreateSubject(); subject.Initialize(); - Action act; - if (async) - { - act = () => subject.SelectServerAsync(null, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.SelectServer(null, CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.SelectServerAsync(OperationContext.NoTimeout, null)) : + Record.Exception(() => subject.SelectServer(OperationContext.NoTimeout, null)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void SelectServer_should_return_a_server_if_one_matches( + public async Task SelectServer_should_return_a_server_if_one_matches( [Values(false, true)] bool async) { @@ -200,15 +182,9 @@ public void SelectServer_should_return_a_server_if_one_matches( var selector = new DelegateServerSelector((c, s) => s); - IServer result; - if (async) - { - result = subject.SelectServerAsync(selector, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - result = subject.SelectServer(selector, CancellationToken.None); - } + var result = async ? + await subject.SelectServerAsync(OperationContext.NoTimeout, selector) : + subject.SelectServer(OperationContext.NoTimeout, selector); result.Should().NotBeNull(); @@ -219,7 +195,7 @@ public void SelectServer_should_return_a_server_if_one_matches( [Theory] [ParameterAttributeData] - public void SelectServer_should_return_second_server_if_first_cannot_be_found( + public async Task SelectServer_should_return_second_server_if_first_cannot_be_found( [Values(false, true)] bool async) { @@ -236,15 +212,10 @@ public void SelectServer_should_return_second_server_if_first_cannot_be_found( var selector = new DelegateServerSelector((c, s) => s); - IServer result; - if (async) - { - result = subject.SelectServerAsync(selector, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - result = subject.SelectServer(selector, CancellationToken.None); - } + var result = async ? + await subject.SelectServerAsync(OperationContext.NoTimeout, selector) : + subject.SelectServer(OperationContext.NoTimeout, selector); + result.Should().NotBeNull(); result.EndPoint.Should().Be(endPoint2); @@ -256,11 +227,11 @@ public void SelectServer_should_return_second_server_if_first_cannot_be_found( [Theory] [ParameterAttributeData] - public void SelectServer_should_throw_if_no_servers_match( + public async Task SelectServer_should_throw_if_no_servers_match( [Values(false, true)] bool async) { - var subject = CreateSubject(); + var subject = CreateSubject(serverSelectionTimeout: TimeSpan.FromMilliseconds(10)); subject.Initialize(); var connected = ServerDescriptionHelper.Connected(subject.Description.ClusterId); @@ -269,17 +240,11 @@ public void SelectServer_should_throw_if_no_servers_match( var selector = new DelegateServerSelector((c, s) => Enumerable.Empty()); - Action act; - if (async) - { - act = () => subject.SelectServerAsync(selector, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.SelectServer(selector, CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.SelectServerAsync(OperationContext.NoTimeout, selector)) : + Record.Exception(() => subject.SelectServer(OperationContext.NoTimeout, selector)); - act.ShouldThrow(); + exception.Should().BeOfType(); _capturedEvents.Next().Should().BeOfType(); _capturedEvents.Next().Should().BeOfType(); @@ -289,7 +254,7 @@ public void SelectServer_should_throw_if_no_servers_match( [Theory] [ParameterAttributeData] - public void SelectServer_should_throw_if_the_matched_server_cannot_be_found_and_no_others_matched( + public async Task SelectServer_should_throw_if_the_matched_server_cannot_be_found_and_no_others_matched( [Values(false, true)] bool async) { @@ -303,17 +268,11 @@ public void SelectServer_should_throw_if_the_matched_server_cannot_be_found_and_ var selector = new DelegateServerSelector((c, s) => s); - Action act; - if (async) - { - act = () => subject.SelectServerAsync(selector, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.SelectServer(selector, CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.SelectServerAsync(OperationContext.NoTimeout, selector)) : + Record.Exception(() => subject.SelectServer(OperationContext.NoTimeout, selector)); - act.ShouldThrow(); + exception.Should().BeOfType(); _capturedEvents.Next().Should().BeOfType(); _capturedEvents.Next().Should().BeOfType(); @@ -326,7 +285,7 @@ public void SelectServer_should_throw_if_the_matched_server_cannot_be_found_and_ [InlineData(0, 0, true)] [InlineData(28, 29, false)] [InlineData(28, 29, true)] - public void SelectServer_should_throw_if_any_servers_are_incompatible(int min, int max, bool async) + public async Task SelectServer_should_throw_if_any_servers_are_incompatible(int min, int max, bool async) { var subject = CreateSubject(); subject.Initialize(); @@ -337,17 +296,11 @@ public void SelectServer_should_throw_if_any_servers_are_incompatible(int min, i var selector = new DelegateServerSelector((c, s) => s); - Action act; - if (async) - { - act = () => subject.SelectServerAsync(selector, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => subject.SelectServer(selector, CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.SelectServerAsync(OperationContext.NoTimeout, selector)) : + Record.Exception(() => subject.SelectServer(OperationContext.NoTimeout, selector)); - act.ShouldThrow(); + exception.Should().BeOfType(); _capturedEvents.Next().Should().BeOfType(); _capturedEvents.Next().Should().BeOfType(); @@ -356,7 +309,7 @@ public void SelectServer_should_throw_if_any_servers_are_incompatible(int min, i [Theory] [ParameterAttributeData] - public void SelectServer_should_keep_trying_to_match_by_waiting_on_cluster_description_changes( + public async Task SelectServer_should_keep_trying_to_match_by_waiting_on_cluster_description_changes( [Values(false, true)] bool async) { @@ -369,7 +322,7 @@ public void SelectServer_should_keep_trying_to_match_by_waiting_on_cluster_descr subject.SetServerDescriptions(connecting); _capturedEvents.Clear(); - Task.Run(() => + _ = Task.Run(() => { _capturedEvents.WaitForEventOrThrowIfTimeout(TimeSpan.FromSeconds(1)); @@ -384,15 +337,9 @@ public void SelectServer_should_keep_trying_to_match_by_waiting_on_cluster_descr var selector = new DelegateServerSelector((c, s) => s); - IServer result; - if (async) - { - result = subject.SelectServerAsync(selector, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - result = subject.SelectServer(selector, CancellationToken.None); - } + var result = async ? + await subject.SelectServerAsync(OperationContext.NoTimeout, selector) : + subject.SelectServer(OperationContext.NoTimeout, selector); result.Should().NotBeNull(); @@ -429,15 +376,9 @@ public async Task SelectServer_should_ignore_deprioritized_servers_if_cluster_is { _capturedEvents.Clear(); - IServer result; - if (async) - { - result = await subject.SelectServerAsync(selector, CancellationToken.None); - } - else - { - result = subject.SelectServer(selector, CancellationToken.None); - } + var result = async ? + await subject.SelectServerAsync(OperationContext.NoTimeout, selector) : + subject.SelectServer(OperationContext.NoTimeout, selector); result.Should().NotBeNull(); @@ -469,15 +410,9 @@ public async Task SelectServer_should_return_deprioritized_servers_if_no_other_s var selector = new PriorityServerSelector(deprioritizedServers); _capturedEvents.Clear(); - IServer result; - if (async) - { - result = await subject.SelectServerAsync(selector, CancellationToken.None); - } - else - { - result = subject.SelectServer(selector, CancellationToken.None); - } + var result = async ? + await subject.SelectServerAsync(OperationContext.NoTimeout, selector) : + subject.SelectServer(OperationContext.NoTimeout, selector); result.Should().NotBeNull(); @@ -525,7 +460,7 @@ public void DescriptionChanged_should_be_raised_when_the_description_changes() [Theory] [ParameterAttributeData] - public void SelectServer_should_apply_both_pre_and_post_server_selectors( + public async Task SelectServer_should_apply_both_pre_and_post_server_selectors( [Values(false, true)] bool async) { @@ -556,15 +491,9 @@ public void SelectServer_should_apply_both_pre_and_post_server_selectors( ServerDescriptionHelper.Connected(subject.Description.ClusterId, new DnsEndPoint("localhost", 27020))); _capturedEvents.Clear(); - IServer result; - if (async) - { - result = subject.SelectServerAsync(middleSelector, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - result = subject.SelectServer(middleSelector, CancellationToken.None); - } + var result = async ? + await subject.SelectServerAsync(OperationContext.NoTimeout, middleSelector) : + subject.SelectServer(OperationContext.NoTimeout, middleSelector); ((DnsEndPoint)result.EndPoint).Port.Should().Be(27020); _capturedEvents.Next().Should().BeOfType(); @@ -574,7 +503,7 @@ public void SelectServer_should_apply_both_pre_and_post_server_selectors( [Theory] [ParameterAttributeData] - public void SelectServer_should_call_custom_selector( + public async Task SelectServer_should_call_custom_selector( [Values(true, false)] bool withEligibleServers, [Values(true, false)] bool async) { @@ -596,10 +525,10 @@ public void SelectServer_should_call_custom_selector( if (withEligibleServers) { - var selectedServer = SelectServerAttempt( - subject, - new DelegateServerSelector((c, s) => s), // do not filter servers - async); + var selector = new DelegateServerSelector((c, s) => s); + var selectedServer = async ? + await subject.SelectServerAsync(OperationContext.NoTimeout, selector): + subject.SelectServer(OperationContext.NoTimeout, selector); var selectedServerPort = ((DnsEndPoint)selectedServer.EndPoint).Port; selectedServerPort.Should().Be(27020); @@ -608,12 +537,10 @@ public void SelectServer_should_call_custom_selector( } else { - var exception = Record.Exception( - () => - SelectServerAttempt( - subject, - new DelegateServerSelector((c, s) => new ServerDescription[0]), // no eligible servers - async)); + var selector = new DelegateServerSelector((c, s) => new ServerDescription[0]); + var exception = async ? + await Record.ExceptionAsync(() => subject.SelectServerAsync(OperationContext.NoTimeout, selector)) : + Record.Exception(() => subject.SelectServer(OperationContext.NoTimeout, selector)); exception.Should().BeOfType(); _capturedEvents.Next().Should().BeOfType(); @@ -636,21 +563,6 @@ private StubCluster CreateSubject(TimeSpan? serverSelectionTimeout = null, Clust return new StubCluster(_settings, _mockServerFactory.Object, _capturedEvents, LoggerFactory, clusterType); } - private IServer SelectServerAttempt(Cluster cluster, IServerSelector operationSelector, bool async) - { - if (async) - { - return cluster - .SelectServerAsync(operationSelector, CancellationToken.None) - .GetAwaiter() - .GetResult(); - } - else - { - return cluster.SelectServer(operationSelector, CancellationToken.None); - } - } - // nested types private class StubCluster : Cluster { diff --git a/tests/MongoDB.Driver.Tests/Core/Clusters/LoadBalancedClusterTests.cs b/tests/MongoDB.Driver.Tests/Core/Clusters/LoadBalancedClusterTests.cs index c5f65fe9fa5..2c14c12f751 100644 --- a/tests/MongoDB.Driver.Tests/Core/Clusters/LoadBalancedClusterTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Clusters/LoadBalancedClusterTests.cs @@ -17,6 +17,7 @@ using System.Linq; using System.Net; using System.Threading; +using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson.TestHelpers; using MongoDB.Driver.Core.Clusters; @@ -63,8 +64,8 @@ public void Constructor_should_initialize_instance() } [Theory] - [ParameterAttributeData()] - public void Constructor_should_handle_directConnection_correctly([Values(null, false, true)]bool directConnection) + [ParameterAttributeData] + public void Constructor_should_handle_directConnection_correctly([Values(false, true)]bool directConnection) { _settings = _settings.With(directConnection: directConnection); @@ -81,7 +82,7 @@ public void Constructor_should_handle_directConnection_correctly([Values(null, f } [Theory] - [ParameterAttributeData()] + [ParameterAttributeData] public void Constructor_should_handle_loadBalanced_correctly([Values(false, true)] bool loadBalanced) { _settings = _settings.With(loadBalanced: loadBalanced); @@ -309,7 +310,7 @@ public void ProcessDnsResults_should_throw_when_srv_records_number_is_unexpected [Theory] [ParameterAttributeData] - public void SelectServer_should_return_expected_server( + public async Task SelectServer_should_return_expected_server( [Values(ConnectionStringScheme.MongoDB, ConnectionStringScheme.MongoDBPlusSrv)] ConnectionStringScheme connectionStringScheme, [Values(false, true)] bool async) { @@ -326,15 +327,9 @@ public void SelectServer_should_return_expected_server( PublishDescription(_endPoint); - IServer result; - if (async) - { - result = subject.SelectServerAsync(Mock.Of(), CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - result = subject.SelectServer(Mock.Of(), CancellationToken.None); - } + var result = async ? + await subject.SelectServerAsync(OperationContext.NoTimeout, Mock.Of()) : + subject.SelectServer(OperationContext.NoTimeout, Mock.Of()); result.EndPoint.Should().Be(_endPoint); } @@ -342,7 +337,7 @@ public void SelectServer_should_return_expected_server( [Theory] [ParameterAttributeData] - public void SelectServer_should_throw_server_selection_timeout_if_server_has_not_been_created_in_time( + public async Task SelectServer_should_throw_server_selection_timeout_if_server_has_not_been_created_in_time( [Values(ConnectionStringScheme.MongoDB, ConnectionStringScheme.MongoDBPlusSrv)] ConnectionStringScheme connectionStringScheme, [Values(false, true)] bool async) { @@ -356,19 +351,13 @@ public void SelectServer_should_throw_server_selection_timeout_if_server_has_not var dnsException = new Exception("Dns exception"); if (connectionStringScheme == ConnectionStringScheme.MongoDBPlusSrv) { - // it has affect only on srv mode + // it has an effect only on srv mode PublishDnsException(subject, dnsException); } - Exception exception; - if (async) - { - exception = Record.Exception(() => subject.SelectServerAsync(Mock.Of(), CancellationToken.None).GetAwaiter().GetResult()); - } - else - { - exception = Record.Exception(() => subject.SelectServer(Mock.Of(), CancellationToken.None)); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.SelectServerAsync(OperationContext.NoTimeout, Mock.Of())) : + Record.Exception(() => subject.SelectServer(OperationContext.NoTimeout, Mock.Of())); var ex = exception.Should().BeOfType().Subject; ex.Message.Should().StartWith($"A timeout occurred after {serverSelectionTimeout.TotalMilliseconds}ms selecting a server. Client view of cluster state is "); @@ -385,7 +374,7 @@ public void SelectServer_should_throw_server_selection_timeout_if_server_has_not [Theory] [ParameterAttributeData] - public void SelectServer_should_be_cancelled_by_cancellationToken( + public async Task SelectServer_should_be_cancelled_by_cancellationToken( [Values(ConnectionStringScheme.MongoDB, ConnectionStringScheme.MongoDBPlusSrv)] ConnectionStringScheme connectionStringScheme, [Values(false, true)] bool async) { @@ -398,14 +387,10 @@ public void SelectServer_should_be_cancelled_by_cancellationToken( Exception exception; using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(100))) { - if (async) - { - exception = Record.Exception(() => subject.SelectServerAsync(Mock.Of(), cancellationTokenSource.Token).GetAwaiter().GetResult()); - } - else - { - exception = Record.Exception(() => subject.SelectServer(Mock.Of(), cancellationTokenSource.Token)); - } + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationTokenSource.Token); + exception = async ? + await Record.ExceptionAsync(() => subject.SelectServerAsync(operationContext, Mock.Of())) : + Record.Exception(() => subject.SelectServer(operationContext, Mock.Of())); } exception.Should().BeOfType(); diff --git a/tests/MongoDB.Driver.Tests/Core/ConnectionPools/ExclusiveConnectionPoolTests.cs b/tests/MongoDB.Driver.Tests/Core/ConnectionPools/ExclusiveConnectionPoolTests.cs index 56570e094f9..e467384ef70 100644 --- a/tests/MongoDB.Driver.Tests/Core/ConnectionPools/ExclusiveConnectionPoolTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/ConnectionPools/ExclusiveConnectionPoolTests.cs @@ -175,7 +175,7 @@ public void AcquireConnection_should_iterate_over_all_dormant_connections() // acquire all connections and return them var allConnections = Enumerable.Range(0, connectionsCount) - .Select(i => subject.AcquireConnection(default)) + .Select(i => subject.AcquireConnection(OperationContext.NoTimeout)) .ToArray(); var connectionNotToExpire = allConnections[allConnections.Length / 2].ConnectionId; @@ -216,22 +216,17 @@ public void Constructor_should_throw_when_exceptionHandler_is_null() [Theory] [ParameterAttributeData] - public void AcquireConnection_should_throw_an_InvalidOperationException_if_not_initialized( + public async Task AcquireConnection_should_throw_an_InvalidOperationException_if_not_initialized( [Values(false, true)] bool async) { _capturedEvents.Clear(); - Action act; - if (async) - { - act = () => _subject.AcquireConnectionAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => _subject.AcquireConnection(CancellationToken.None); - } - act.ShouldThrow(); + var exception = async ? + await Record.ExceptionAsync(() => _subject.AcquireConnectionAsync(OperationContext.NoTimeout)) : + Record.Exception(() => _subject.AcquireConnection(OperationContext.NoTimeout)); + + exception.Should().BeOfType(); _capturedEvents.Next().Should().BeOfType(); var connectionPoolCheckingOutConnectionFailedEvent = _capturedEvents.Next(); var e = connectionPoolCheckingOutConnectionFailedEvent.Should().BeOfType().Subject; @@ -241,24 +236,19 @@ public void AcquireConnection_should_throw_an_InvalidOperationException_if_not_i [Theory] [ParameterAttributeData] - public void AcquireConnection_should_throw_an_ObjectDisposedException_after_disposing( + public async Task AcquireConnection_should_throw_an_ObjectDisposedException_after_disposing( [Values(false, true)] bool async) { _capturedEvents.Clear(); _subject.Dispose(); - Action act; - if (async) - { - act = () => _subject.AcquireConnectionAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => _subject.AcquireConnection(CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => _subject.AcquireConnectionAsync(OperationContext.NoTimeout)) : + Record.Exception(() => _subject.AcquireConnection(OperationContext.NoTimeout)); + + exception.Should().BeOfType(); - act.ShouldThrow(); _capturedEvents.Next().Should().BeOfType(); _capturedEvents.Next().Should().BeOfType(); _capturedEvents.Next().Should().BeOfType(); @@ -270,22 +260,16 @@ public void AcquireConnection_should_throw_an_ObjectDisposedException_after_disp [Theory] [ParameterAttributeData] - public void AcquireConnection_should_return_a_connection( + public async Task AcquireConnection_should_return_a_connection( [Values(false, true)] bool async) { InitializeAndWait(); _capturedEvents.Clear(); - IConnectionHandle connection; - if (async) - { - connection = _subject.AcquireConnectionAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - connection = _subject.AcquireConnection(CancellationToken.None); - } + var connection = async ? + await _subject.AcquireConnectionAsync(OperationContext.NoTimeout) : + _subject.AcquireConnection(OperationContext.NoTimeout); connection.Should().NotBeNull(); _subject.AvailableCount.Should().Be(_settings.MaxConnections - 1); @@ -345,27 +329,18 @@ public async Task AcquireConnection_should_invoke_error_handling_before_releasin subject.Initialize(); subject.SetReady(); - try - { - if (async) - { - _ = await subject.AcquireConnectionAsync(default); - } - else - { - _ = subject.AcquireConnection(default); - } - } - catch (MongoConnectionException) - { - subject.AvailableCount.Should().Be(maxConnections); - mockConnectionExceptionHandler.Verify(handler => handler.HandleExceptionOnOpen(exception), Times.Once); - } + var resultException = async ? + await Record.ExceptionAsync(() => subject.AcquireConnectionAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.AcquireConnection(OperationContext.NoTimeout)); + + resultException.Should().BeOfType(); + subject.AvailableCount.Should().Be(maxConnections); + mockConnectionExceptionHandler.Verify(handler => handler.HandleExceptionOnOpen(exception), Times.Once); } [Theory] [ParameterAttributeData] - internal void AcquireConnection_should_track_checked_out_reasons( + internal async Task AcquireConnection_should_track_checked_out_reasons( [Values(CheckOutReason.Cursor, CheckOutReason.Transaction)] CheckOutReason reason, [Values(1, 3, 5)] int attempts, [Values(false, true)] bool async) @@ -390,15 +365,9 @@ internal void AcquireConnection_should_track_checked_out_reasons( List connections = new(); for (int attempt = 1; attempt <= attempts; attempt++) { - IConnectionHandle connection; - if (async) - { - connection = subject.AcquireConnectionAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - connection = subject.AcquireConnection(CancellationToken.None); - } + var connection = async ? + await subject.AcquireConnectionAsync(OperationContext.NoTimeout) : + subject.AcquireConnection(OperationContext.NoTimeout); ((ICheckOutReasonTracker)connection).SetCheckOutReasonIfNotAlreadySet(reason); connections.Add(connection); @@ -448,7 +417,7 @@ IEnumerable GetEnumItemsExcept(CheckOutReason reason) [Theory] [ParameterAttributeData] - public void AcquireConnection_should_increase_count_up_to_the_max_number_of_connections( + public async Task AcquireConnection_should_increase_count_up_to_the_max_number_of_connections( [Values(false, true)] bool async) { @@ -459,15 +428,9 @@ public void AcquireConnection_should_increase_count_up_to_the_max_number_of_conn for (int i = 0; i < _settings.MaxConnections; i++) { - IConnection connection; - if (async) - { - connection = _subject.AcquireConnectionAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - connection = _subject.AcquireConnection(CancellationToken.None); - } + var connection = async ? + await _subject.AcquireConnectionAsync(OperationContext.NoTimeout) : + _subject.AcquireConnection(OperationContext.NoTimeout); connections.Add(connection); } @@ -494,21 +457,15 @@ public void AcquireConnection_should_increase_count_up_to_the_max_number_of_conn [Theory] [ParameterAttributeData] - public void AcquiredConnection_should_return_connections_to_the_pool_when_disposed( + public async Task AcquiredConnection_should_return_connections_to_the_pool_when_disposed( [Values(false, true)] bool async) { InitializeAndWait(); - IConnectionHandle connection; - if (async) - { - connection = _subject.AcquireConnectionAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - connection = _subject.AcquireConnection(CancellationToken.None); - } + var connection = async ? + await _subject.AcquireConnectionAsync(OperationContext.NoTimeout) : + _subject.AcquireConnection(OperationContext.NoTimeout); _capturedEvents.Clear(); @@ -523,7 +480,7 @@ public void AcquiredConnection_should_return_connections_to_the_pool_when_dispos [Theory] [ParameterAttributeData] - public void AcquiredConnection_should_not_return_connections_to_the_pool_when_disposed_and_expired( + public async Task AcquiredConnection_should_not_return_connections_to_the_pool_when_disposed_and_expired( [Values(false, true)] bool async) { @@ -538,15 +495,9 @@ public void AcquiredConnection_should_not_return_connections_to_the_pool_when_di InitializeAndWait(); - IConnectionHandle connection; - if (async) - { - connection = _subject.AcquireConnectionAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - connection = _subject.AcquireConnection(CancellationToken.None); - } + var connection = async ? + await _subject.AcquireConnectionAsync(OperationContext.NoTimeout) : + _subject.AcquireConnection(OperationContext.NoTimeout); _capturedEvents.Clear(); @@ -566,7 +517,7 @@ public void AcquiredConnection_should_not_return_connections_to_the_pool_when_di [Theory] [ParameterAttributeData] - public void AcquireConnection_should_throw_a_TimeoutException_when_all_connections_are_checked_out( + public async Task AcquireConnection_should_throw_a_TimeoutException_when_all_connections_are_checked_out( [Values(false, true)] bool async) { @@ -574,30 +525,18 @@ public void AcquireConnection_should_throw_a_TimeoutException_when_all_connectio var connections = new List(); for (int i = 0; i < _settings.MaxConnections; i++) { - IConnection connection; - if (async) - { - connection = _subject.AcquireConnectionAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - connection = _subject.AcquireConnection(CancellationToken.None); - } + var connection = async ? + await _subject.AcquireConnectionAsync(OperationContext.NoTimeout) : + _subject.AcquireConnection(OperationContext.NoTimeout); connections.Add(connection); } _capturedEvents.Clear(); - Action act; - if (async) - { - act = () => _subject.AcquireConnectionAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => _subject.AcquireConnection(CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => _subject.AcquireConnectionAsync(OperationContext.NoTimeout)) : + Record.Exception(() => _subject.AcquireConnection(OperationContext.NoTimeout)); - act.ShouldThrow(); + exception.Should().BeOfType(); _capturedEvents.Next().Should().BeOfType(); var connectionPoolCheckingOutConnectionFailedEvent = _capturedEvents.Next(); @@ -681,7 +620,6 @@ public void AcquireConnection_should_timeout_when_non_sufficient_reused_connecti // block further establishments blockEstablishmentEvent.Reset(); - var allConnections = new List(); var actualTimeouts = 0; var expectedTimeouts = maxAcquiringCount - maxConnecting; @@ -735,7 +673,7 @@ public void AcquireConnection_should_timeout_when_non_sufficient_reused_connecti [Theory] [ParameterAttributeData] - public void AcquiredConnection_should_not_throw_exceptions_when_disposed_after_the_pool_was_disposed( + public async Task AcquiredConnection_should_not_throw_exceptions_when_disposed_after_the_pool_was_disposed( [Values(false, true)] bool async) { @@ -744,13 +682,13 @@ public void AcquiredConnection_should_not_throw_exceptions_when_disposed_after_t IConnectionHandle connection2; if (async) { - connection1 = _subject.AcquireConnectionAsync(CancellationToken.None).GetAwaiter().GetResult(); - connection2 = _subject.AcquireConnectionAsync(CancellationToken.None).GetAwaiter().GetResult(); + connection1 = await _subject.AcquireConnectionAsync(OperationContext.NoTimeout); + connection2 = await _subject.AcquireConnectionAsync(OperationContext.NoTimeout); } else { - connection1 = _subject.AcquireConnection(CancellationToken.None); - connection2 = _subject.AcquireConnection(CancellationToken.None); + connection1 = _subject.AcquireConnection(OperationContext.NoTimeout); + connection2 = _subject.AcquireConnection(OperationContext.NoTimeout); } _capturedEvents.Clear(); @@ -916,22 +854,16 @@ public void Clear_should_throw_an_ObjectDisposedException_after_disposing() [Theory] [ParameterAttributeData] - public void Clear_should_cause_existing_connections_to_be_expired( + public async Task Clear_should_cause_existing_connections_to_be_expired( [Values(false, true)] bool async) { _subject.Initialize(); _subject.SetReady(); - IConnectionHandle connection; - if (async) - { - connection = _subject.AcquireConnectionAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - connection = _subject.AcquireConnection(CancellationToken.None); - } + var connection = async ? + await _subject.AcquireConnectionAsync(OperationContext.NoTimeout) : + _subject.AcquireConnection(OperationContext.NoTimeout); connection.IsExpired.Should().BeFalse(); _subject.Clear(closeInUseConnections: false); @@ -940,7 +872,7 @@ public void Clear_should_cause_existing_connections_to_be_expired( [Theory] [ParameterAttributeData] - public void Clear_with_serviceId_should_cause_only_expected_connections_to_be_expired( + public async Task Clear_with_serviceId_should_cause_only_expected_connections_to_be_expired( [Values(false, true)] bool async) { var serviceId = ObjectId.GenerateNewId(); @@ -970,7 +902,9 @@ public void Clear_with_serviceId_should_cause_only_expected_connections_to_be_ex subject.Initialize(); subject.SetReady(); - var connection = AcquireConnection(subject, async); + var connection = async ? + await subject.AcquireConnectionAsync(OperationContext.NoTimeout) : + subject.AcquireConnection(OperationContext.NoTimeout); connection.IsExpired.Should().BeFalse(); var randomServiceId = ObjectId.GenerateNewId(); @@ -1686,13 +1620,13 @@ private IConnection AcquireConnection(ExclusiveConnectionPool subject, bool asyn if (async) { return subject - .AcquireConnectionAsync(CancellationToken.None) + .AcquireConnectionAsync(OperationContext.NoTimeout) .GetAwaiter() .GetResult(); } else { - return subject.AcquireConnection(CancellationToken.None); + return subject.AcquireConnection(OperationContext.NoTimeout); } } diff --git a/tests/MongoDB.Driver.Tests/Core/ConnectionPools/MaintenanceHelperTests.cs b/tests/MongoDB.Driver.Tests/Core/ConnectionPools/MaintenanceHelperTests.cs index 4e8a23a13fb..2b4970bd723 100644 --- a/tests/MongoDB.Driver.Tests/Core/ConnectionPools/MaintenanceHelperTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/ConnectionPools/MaintenanceHelperTests.cs @@ -168,7 +168,7 @@ public void Stop_should_trigger_immediate_maintenance_call( IConnection acquiredConnection = null; if (checkOutConnection) { - acquiredConnection = pool.AcquireConnection(CancellationToken.None); + acquiredConnection = pool.AcquireConnection(OperationContext.NoTimeout); acquiredConnection.ConnectionId.LongLocalValue.Should().Be(1); } @@ -178,7 +178,7 @@ public void Stop_should_trigger_immediate_maintenance_call( var requestInPlayTimeout = TimeSpan.FromMilliseconds(100); if (!closeInUseConnection && checkOutConnection) { - // connection in progress should be not touched + // connection in progress should be not touched Thread.Sleep(requestInPlayTimeout); } else diff --git a/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3173Tests.cs b/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3173Tests.cs index bdf4438a008..70ff0840d24 100644 --- a/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3173Tests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3173Tests.cs @@ -75,7 +75,7 @@ public void Ensure_command_network_error_before_handshake_is_correctly_handled([ // The next hello or legacy hello response will be delayed because the waiting in the mock.Callbacks cluster.Initialize(); - var selectedServer = cluster.SelectServer(CreateWritableServerAndEndPointSelector(__endPoint1), CancellationToken.None); + var selectedServer = cluster.SelectServer(OperationContext.NoTimeout, CreateWritableServerAndEndPointSelector(__endPoint1)); initialSelectedEndpoint = selectedServer.EndPoint; initialSelectedEndpoint.Should().Be(__endPoint1); @@ -86,11 +86,11 @@ public void Ensure_command_network_error_before_handshake_is_correctly_handled([ Exception exception; if (async) { - exception = Record.Exception(() => selectedServer.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult()); + exception = Record.Exception(() => selectedServer.GetChannelAsync(OperationContext.NoTimeout).GetAwaiter().GetResult()); } else { - exception = Record.Exception(() => selectedServer.GetChannel(CancellationToken.None)); + exception = Record.Exception(() => selectedServer.GetChannel(OperationContext.NoTimeout)); } var e = exception.Should().BeOfType().Subject; @@ -107,7 +107,7 @@ public void Ensure_command_network_error_before_handshake_is_correctly_handled([ } // ensure that a new server can be selected - selectedServer = cluster.SelectServer(WritableServerSelector.Instance, CancellationToken.None); + selectedServer = cluster.SelectServer(OperationContext.NoTimeout, WritableServerSelector.Instance); // ensure that the selected server is not the same as the initial selectedServer.EndPoint.Should().Be(__endPoint2); @@ -187,11 +187,11 @@ void SetupConnectionPool(Mock mockConnectionPool, IConnectionHa { var dnsException = CreateDnsException(connection.ConnectionId, from: "pool"); mockConnectionPool - .Setup(c => c.AcquireConnection(It.IsAny())) + .Setup(c => c.AcquireConnection(It.IsAny())) .Callback(() => exceptionHandlerProvider().HandleExceptionOnOpen(dnsException)) .Throws(dnsException); // throw command dns exception mockConnectionPool - .Setup(c => c.AcquireConnectionAsync(It.IsAny())) + .Setup(c => c.AcquireConnectionAsync(It.IsAny())) .Callback(() => exceptionHandlerProvider().HandleExceptionOnOpen(dnsException)) .Throws(dnsException); // throw command dns exception } diff --git a/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3302Tests.cs b/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3302Tests.cs index 2a2a3f024d2..a3c5488e56f 100644 --- a/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3302Tests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3302Tests.cs @@ -96,7 +96,7 @@ public async Task RapidHeartbeatTimerCallback_should_ignore_reentrant_calls() cluster.Initialize(); // Trigger Cluster._rapidHeartbeatTimer - var _ = cluster.SelectServerAsync(CreateWritableServerAndEndPointSelector(__endPoint1), CancellationToken.None); + _ = cluster.SelectServerAsync(OperationContext.NoTimeout, CreateWritableServerAndEndPointSelector(__endPoint1)); // Wait for all heartbeats to complete await Task.WhenAny(allHeartbeatsReceived.Task, Task.Delay(1000)); @@ -142,13 +142,13 @@ public async Task Ensure_no_deadlock_after_primary_update() server.DescriptionChanged += ProcessServerDescriptionChanged; } - var selectedServer = cluster.SelectServer(CreateWritableServerAndEndPointSelector(__endPoint1), CancellationToken.None); + var selectedServer = cluster.SelectServer(OperationContext.NoTimeout, CreateWritableServerAndEndPointSelector(__endPoint1)); initialSelectedEndpoint = selectedServer.EndPoint; initialSelectedEndpoint.Should().Be(__endPoint1); // Change primary currentPrimaries.Add(__serverId2); - selectedServer = cluster.SelectServer(CreateWritableServerAndEndPointSelector(__endPoint2), CancellationToken.None); + selectedServer = cluster.SelectServer(OperationContext.NoTimeout, CreateWritableServerAndEndPointSelector(__endPoint2)); selectedServer.EndPoint.Should().Be(__endPoint2); // Ensure stalling happened @@ -198,10 +198,10 @@ void SetupConnection(Mock mockConnectionHandle, ServerId serv void SetupConnectionPool(Mock mockConnectionPool, IConnectionHandle connection) { mockConnectionPool - .Setup(c => c.AcquireConnection(It.IsAny())) + .Setup(c => c.AcquireConnection(It.IsAny())) .Returns(connection); mockConnectionPool - .Setup(c => c.AcquireConnectionAsync(It.IsAny())) + .Setup(c => c.AcquireConnectionAsync(It.IsAny())) .Returns(Task.FromResult(connection)); } diff --git a/tests/MongoDB.Driver.Tests/Core/LoadBalancingIntergationTests.cs b/tests/MongoDB.Driver.Tests/Core/LoadBalancingIntergationTests.cs index d8a6095aaf1..a433e87fa5e 100644 --- a/tests/MongoDB.Driver.Tests/Core/LoadBalancingIntergationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/LoadBalancingIntergationTests.cs @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; -using System.Threading; using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; @@ -615,11 +614,11 @@ private BulkWriteOperationResult CreateAndRunBulkOperation(RetryableWriteContext if (async) { - return bulkInsertOperation.ExecuteAsync(context, CancellationToken.None).GetAwaiter().GetResult(); + return bulkInsertOperation.ExecuteAsync(OperationContext.NoTimeout, context).GetAwaiter().GetResult(); } else { - return bulkInsertOperation.Execute(context, CancellationToken.None); + return bulkInsertOperation.Execute(OperationContext.NoTimeout, context); } } @@ -635,19 +634,19 @@ private IAsyncCursor CreateAndRunFindOperation(RetryableReadContex if (async) { - return findOperation.ExecuteAsync(context, CancellationToken.None).GetAwaiter().GetResult(); + return findOperation.ExecuteAsync(OperationContext.NoTimeout, context).GetAwaiter().GetResult(); } else { - return findOperation.Execute(context, CancellationToken.None); + return findOperation.Execute(OperationContext.NoTimeout, context); } } private RetryableReadContext CreateRetryableReadContext(IReadBindingHandle readBindingHandle, bool async) { return async - ? RetryableReadContext.CreateAsync(readBindingHandle, retryRequested: false, CancellationToken.None).GetAwaiter().GetResult() - : RetryableReadContext.Create(readBindingHandle, retryRequested: false, CancellationToken.None); + ? RetryableReadContext.CreateAsync(OperationContext.NoTimeout, readBindingHandle, retryRequested: false).GetAwaiter().GetResult() + : RetryableReadContext.Create(OperationContext.NoTimeout, readBindingHandle, retryRequested: false); } private DisposableBindingBundle CreateReadBindingsAndRetryableReadContext(IClusterInternal cluster, ICoreSessionHandle sessionHandle, bool async) @@ -663,8 +662,8 @@ private DisposableBindingBundle Create private RetryableWriteContext CreateRetryableWriteContext(IReadWriteBindingHandle readWriteBindingHandle, bool async) { return async - ? RetryableWriteContext.CreateAsync(readWriteBindingHandle, retryRequested: false, CancellationToken.None).GetAwaiter().GetResult() - : RetryableWriteContext.Create(readWriteBindingHandle, retryRequested: false, CancellationToken.None); + ? RetryableWriteContext.CreateAsync(OperationContext.NoTimeout, readWriteBindingHandle, retryRequested: false).GetAwaiter().GetResult() + : RetryableWriteContext.Create(OperationContext.NoTimeout, readWriteBindingHandle, retryRequested: false); } private DisposableBindingBundle CreateReadWriteBindingsAndRetryableWriteContext(IClusterInternal cluster, ICoreSessionHandle sessionHandle, bool async) diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/AggregateOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/AggregateOperationTests.cs index 9e20b7d8fa0..cf3cc8b3206 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/AggregateOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/AggregateOperationTests.cs @@ -592,16 +592,7 @@ public void Execute_should_throw_when_binding_is_null( { var subject = new AggregateOperation(_collectionNamespace, __pipeline, __resultSerializer, _messageEncoderSettings); - Exception exception; - if (async) - { - exception = Record.Exception(() => subject.ExecuteAsync(binding: null, cancellationToken: CancellationToken.None).GetAwaiter().GetResult()); - } - else - { - exception = Record.Exception(() => subject.Execute(binding: null, cancellationToken: CancellationToken.None)); - } - + var exception = Record.Exception(() => ExecuteOperation(subject, binding: null, async: async)); var argumentNullException = exception.Should().BeOfType().Subject; argumentNullException.ParamName.Should().Be("binding"); } diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/AsyncCursorTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/AsyncCursorTests.cs index 1f5be9d301d..a1b8943a276 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/AsyncCursorTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/AsyncCursorTests.cs @@ -328,7 +328,7 @@ public void Dispose_should_be_shielded_from_exceptions() { var mockChannelSource = new Mock(); mockChannelSource - .Setup(c => c.GetChannel(It.IsAny())) + .Setup(c => c.GetChannel(It.IsAny())) .Throws(); var subject = CreateSubject(cursorId: 1, channelSource: Optional.Create(mockChannelSource.Object)); @@ -341,7 +341,7 @@ public void Dispose_should_dispose_channel_source_when_cursor_was_not_closed_by_ { var mockChannelSource = new Mock(); mockChannelSource - .Setup(c => c.GetChannel(It.IsAny())) + .Setup(c => c.GetChannel(It.IsAny())) .Throws(); var subject = CreateSubject(cursorId: 1, channelSource: Optional.Create(mockChannelSource.Object)); @@ -385,7 +385,7 @@ public void Dispose_should_not_call_close_cursors_for_zero_cursor_id() var mockChannelSource = new Mock(); mockChannelSource - .Setup(c => c.GetChannel(It.IsAny())) + .Setup(c => c.GetChannel(It.IsAny())) .Returns(mockChannelHandle.Object); var subject = CreateSubject(cursorId: 0, channelSource: Optional.Create(mockChannelSource.Object)); @@ -409,8 +409,6 @@ public void GetMore_should_use_same_session( var collectionNamespace = new CollectionNamespace(databaseNamespace, "collection"); var cursorId = 1; var subject = CreateSubject(collectionNamespace: collectionNamespace, cursorId: cursorId, channelSource: Optional.Create(channelSource)); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; var connectionDescription = CreateConnectionDescriptionSupportingSession(); mockChannelSource.SetupGet(m => m.Session).Returns(session); @@ -427,11 +425,11 @@ public void GetMore_should_use_same_session( } }; - subject.MoveNext(cancellationToken); // skip empty first batch + subject.MoveNext(CancellationToken.None); // skip empty first batch var sameSessionWasUsed = false; if (async) { - mockChannelSource.Setup(m => m.GetChannelAsync(cancellationToken)).Returns(Task.FromResult(channel)); + mockChannelSource.Setup(m => m.GetChannelAsync(It.IsAny())).Returns(Task.FromResult(channel)); mockChannel .Setup(m => m.CommandAsync( session, @@ -445,15 +443,15 @@ public void GetMore_should_use_same_session( CommandResponseHandling.Return, It.IsAny>(), It.IsAny(), - cancellationToken)) + It.IsAny())) .Callback(() => sameSessionWasUsed = true) .Returns(Task.FromResult(secondBatch)); - subject.MoveNextAsync(cancellationToken).GetAwaiter().GetResult(); + subject.MoveNextAsync(CancellationToken.None).GetAwaiter().GetResult(); } else { - mockChannelSource.Setup(m => m.GetChannel(cancellationToken)).Returns(channel); + mockChannelSource.Setup(m => m.GetChannel(It.IsAny())).Returns(channel); mockChannel .Setup(m => m.Command( session, @@ -467,29 +465,17 @@ public void GetMore_should_use_same_session( CommandResponseHandling.Return, It.IsAny>(), It.IsAny(), - cancellationToken)) + It.IsAny())) .Callback(() => sameSessionWasUsed = true) .Returns(secondBatch); - subject.MoveNext(cancellationToken); + subject.MoveNext(CancellationToken.None); } sameSessionWasUsed.Should().BeTrue(); } // private methods - private void Close(AsyncCursor asyncCursor, bool async, CancellationToken cancellationToken) - { - if (async) - { - asyncCursor.CloseAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - asyncCursor.Close(cancellationToken); - } - } - private ConnectionDescription CreateConnectionDescriptionSupportingSession(int maxWireVersion = WireVersion.Server36) { var clusterId = new ClusterId(1); @@ -529,18 +515,6 @@ private AsyncCursor CreateSubject( maxTime.WithDefault(null)); } - private bool MoveNext(IAsyncCursor asyncCursor, bool async, CancellationToken cancellationToken) - { - if (async) - { - return asyncCursor.MoveNextAsync(cancellationToken).GetAwaiter().GetResult(); - } - else - { - return asyncCursor.MoveNext(cancellationToken); - } - } - private void SetupChannelMocks(Mock mockChannelSource, Mock mockChannelHandle, bool async, string commandResult, int maxWireVersion = WireVersion.Server36, bool isChannelExpired = false) { SetupChannelMocks(mockChannelSource, mockChannelHandle, async, BsonDocument.Parse(commandResult), maxWireVersion, isChannelExpired); @@ -563,7 +537,7 @@ private void SetupChannelMocks(Mock mockChannelSource, Mock c.GetChannelAsync(It.IsAny())) + .Setup(c => c.GetChannelAsync(It.IsAny())) .ReturnsAsync(mockChannelHandle.Object); mockChannelHandle @@ -590,7 +564,7 @@ private void SetupChannelMocks(Mock mockChannelSource, Mock c.GetChannel(It.IsAny())) + .Setup(c => c.GetChannel(It.IsAny())) .Returns(mockChannelHandle.Object); mockChannelHandle @@ -676,19 +650,18 @@ public void Session_reference_count_should_be_decremented_as_soon_as_possible(in Insert(documents); _session.ReferenceCount().Should().Be(1); - var cancellationToken = CancellationToken.None; using (var binding = new ReadPreferenceBinding(CoreTestConfiguration.Cluster, ReadPreference.Primary, _session.Fork())) - using (var channelSource = (ChannelSourceHandle)binding.GetReadChannelSource(cancellationToken)) - using (var channel = channelSource.GetChannel(cancellationToken)) + using (var channelSource = (ChannelSourceHandle)binding.GetReadChannelSource(OperationContext.NoTimeout)) + using (var channel = channelSource.GetChannel(OperationContext.NoTimeout)) { var query = new BsonDocument(); long cursorId; - var firstBatch = GetFirstBatch(channel, query, batchSize, cancellationToken, out cursorId); + var firstBatch = GetFirstBatch(channel, query, batchSize, CancellationToken.None, out cursorId); using (var cursor = new AsyncCursor(channelSource, _collectionNamespace, comment: null, firstBatch, cursorId, batchSize, null, BsonDocumentSerializer.Instance, new MessageEncoderSettings())) { AssertExpectedSessionReferenceCount(_session, cursor); - while (cursor.MoveNext(cancellationToken)) + while (cursor.MoveNext(CancellationToken.None)) { AssertExpectedSessionReferenceCount(_session, cursor); } @@ -738,31 +711,6 @@ private IReadOnlyList GetFirstBatchUsingFindCommand(IChannelHandle cursorId = cursor["id"].ToInt64(); return firstBatch; } - - private IReadOnlyList GetFirstBatchUsingQueryMessage(IChannelHandle channel, BsonDocument query, int batchSize, CancellationToken cancellationToken, out long cursorId) - { -#pragma warning disable 618 - var result = channel.Query( - _collectionNamespace, - query, - null, // fields - NoOpElementNameValidator.Instance, - 0, // skip - batchSize, - false, // secondaryOk - false, // partialOk - false, // noCursorTimeout - false, // oplogReplay - false, // tailableCursor - false, // awaitData - BsonDocumentSerializer.Instance, - _messageEncoderSettings, - cancellationToken); -#pragma warning restore 618 - - cursorId = result.CursorId; - return result.Documents; - } } internal static class AsyncCursorReflector diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/BulkMixedWriteOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/BulkMixedWriteOperationTests.cs index fc223f8ab02..c14714121a3 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/BulkMixedWriteOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/BulkMixedWriteOperationTests.cs @@ -1376,8 +1376,8 @@ public void Execute_unacknowledged_with_an_error_in_the_first_batch_and_ordered_ }; using (var readWriteBinding = CreateReadWriteBinding(useImplicitSession: true)) - using (var channelSource = readWriteBinding.GetWriteChannelSource(CancellationToken.None)) - using (var channel = channelSource.GetChannel(CancellationToken.None)) + using (var channelSource = readWriteBinding.GetWriteChannelSource(OperationContext.NoTimeout)) + using (var channel = channelSource.GetChannel(OperationContext.NoTimeout)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, readWriteBinding.Session.Fork())) { var result = ExecuteOperation(subject, channelBinding, async); @@ -1423,8 +1423,8 @@ public void Execute_unacknowledged_with_an_error_in_the_first_batch_and_ordered_ }; using (var readWriteBinding = CreateReadWriteBinding(useImplicitSession: true)) - using (var channelSource = readWriteBinding.GetWriteChannelSource(CancellationToken.None)) - using (var channel = channelSource.GetChannel(CancellationToken.None)) + using (var channelSource = readWriteBinding.GetWriteChannelSource(OperationContext.NoTimeout)) + using (var channel = channelSource.GetChannel(OperationContext.NoTimeout)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, readWriteBinding.Session.Fork())) { var result = ExecuteOperation(subject, channelBinding, async); @@ -1464,8 +1464,8 @@ public void Execute_unacknowledged_with_an_error_in_the_second_batch_and_ordered }; using (var readWriteBinding = CreateReadWriteBinding(useImplicitSession: true)) - using (var channelSource = readWriteBinding.GetWriteChannelSource(CancellationToken.None)) - using (var channel = channelSource.GetChannel(CancellationToken.None)) + using (var channelSource = readWriteBinding.GetWriteChannelSource(OperationContext.NoTimeout)) + using (var channel = channelSource.GetChannel(OperationContext.NoTimeout)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, readWriteBinding.Session.Fork())) { var result = ExecuteOperation(subject, channelBinding, async); @@ -1505,8 +1505,8 @@ public void Execute_unacknowledged_with_an_error_in_the_second_batch_and_ordered }; using (var readWriteBinding = CreateReadWriteBinding(useImplicitSession: true)) - using (var channelSource = readWriteBinding.GetWriteChannelSource(CancellationToken.None)) - using (var channel = channelSource.GetChannel(CancellationToken.None)) + using (var channelSource = readWriteBinding.GetWriteChannelSource(OperationContext.NoTimeout)) + using (var channel = channelSource.GetChannel(OperationContext.NoTimeout)) using (var channelBinding = new ChannelReadWriteBinding(channelSource.Server, channel, readWriteBinding.Session.Fork())) { var result = ExecuteOperation(subject, channelBinding, async); diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/ChangeStreamCursorTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/ChangeStreamCursorTests.cs index f2661693749..4e11447d94c 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/ChangeStreamCursorTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/ChangeStreamCursorTests.cs @@ -435,8 +435,6 @@ public void MoveNext_should_call_Resume_after_resumable_exception( var mockBinding = new Mock(); var mockOperation = new Mock>(); var subject = CreateSubject(cursor: mockCursor.Object, binding: mockBinding.Object, changeStreamOperation: mockOperation.Object); - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; var resumableException = CoreExceptionHelper.CreateException(resumableExceptionType); var mockResumedCursor = CreateMockCursor(); @@ -444,34 +442,34 @@ public void MoveNext_should_call_Resume_after_resumable_exception( var resumeToken = BsonDocument.Parse("{ resumeToken : 1 }"); var firstDocument = BsonDocument.Parse("{ _id : { resumeToken : 1 }, operationType : \"insert\", ns : { db : \"db\", coll : \"coll\" }, documentKey : { _id : 1 }, fullDocument : { _id : 1 } }"); var firstBatch = new[] { ToRawDocument(firstDocument) }; - mockCursor.Setup(c => c.MoveNext(cancellationToken)).Returns(true); + mockCursor.Setup(c => c.MoveNext(It.IsAny())).Returns(true); mockCursor.SetupGet(c => c.Current).Returns(firstBatch); - subject.MoveNext(cancellationToken); + subject.MoveNext(CancellationToken.None); bool result; if (async) { - mockCursor.Setup(c => c.MoveNextAsync(cancellationToken)).Returns(CreateFaultedTask(resumableException)); - mockOperation.Setup(o => o.ResumeAsync(mockBinding.Object, cancellationToken)).Returns(Task.FromResult(mockResumedCursor.Object)); - mockResumedCursor.Setup(c => c.MoveNextAsync(cancellationToken)).Returns(Task.FromResult(expectedResult)); + mockCursor.Setup(c => c.MoveNextAsync(It.IsAny())).Returns(CreateFaultedTask(resumableException)); + mockOperation.Setup(o => o.ResumeAsync(It.IsAny(), mockBinding.Object)).Returns(Task.FromResult(mockResumedCursor.Object)); + mockResumedCursor.Setup(c => c.MoveNextAsync(It.IsAny())).Returns(Task.FromResult(expectedResult)); - result = subject.MoveNextAsync(cancellationToken).GetAwaiter().GetResult(); + result = subject.MoveNextAsync(It.IsAny()).GetAwaiter().GetResult(); - mockCursor.Verify(c => c.MoveNextAsync(cancellationToken), Times.Once); - mockOperation.Verify(o => o.ResumeAsync(mockBinding.Object, cancellationToken), Times.Once); - mockResumedCursor.Verify(c => c.MoveNextAsync(cancellationToken), Times.Once); + mockCursor.Verify(c => c.MoveNextAsync(It.IsAny()), Times.Once); + mockOperation.Verify(o => o.ResumeAsync(It.IsAny(), mockBinding.Object), Times.Once); + mockResumedCursor.Verify(c => c.MoveNextAsync(It.IsAny()), Times.Once); } else { - mockCursor.Setup(c => c.MoveNext(cancellationToken)).Throws(resumableException); - mockOperation.Setup(o => o.Resume(mockBinding.Object, cancellationToken)).Returns(mockResumedCursor.Object); - mockResumedCursor.Setup(c => c.MoveNext(cancellationToken)).Returns(expectedResult); + mockCursor.Setup(c => c.MoveNext(It.IsAny())).Throws(resumableException); + mockOperation.Setup(o => o.Resume(It.IsAny(), mockBinding.Object)).Returns(mockResumedCursor.Object); + mockResumedCursor.Setup(c => c.MoveNext(It.IsAny())).Returns(expectedResult); - result = subject.MoveNext(cancellationToken); + result = subject.MoveNext(It.IsAny()); - mockCursor.Verify(c => c.MoveNext(cancellationToken), Times.Exactly(2)); - mockOperation.Verify(o => o.Resume(mockBinding.Object, cancellationToken), Times.Once); - mockResumedCursor.Verify(c => c.MoveNext(cancellationToken), Times.Once); + mockCursor.Verify(c => c.MoveNext(It.IsAny()), Times.Exactly(2)); + mockOperation.Verify(o => o.Resume(It.IsAny(), mockBinding.Object), Times.Once); + mockResumedCursor.Verify(c => c.MoveNext(It.IsAny()), Times.Once); } result.Should().Be(expectedResult); diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/ChangeStreamOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/ChangeStreamOperationTests.cs index b368fdea6a2..394685a8807 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/ChangeStreamOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/ChangeStreamOperationTests.cs @@ -636,15 +636,7 @@ public void Execute_should_throw_when_binding_does_not_implement_IReadBindingHan var subject = CreateSubject(); var binding = new Mock().Object; - Exception exception; - if (async) - { - exception = Record.Exception(() => subject.ExecuteAsync(binding, CancellationToken.None).GetAwaiter().GetResult()); - } - else - { - exception = Record.Exception(() => subject.Execute(binding, CancellationToken.None)); - } + var exception = Record.Exception(() => ExecuteOperation(subject, binding, async)); var argumentException = exception.Should().BeOfType().Subject; argumentException.ParamName.Should().Be("binding"); diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/CompositeWriteOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/CompositeWriteOperationTests.cs index 18c67618a5c..3dcc8a36cfc 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/CompositeWriteOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/CompositeWriteOperationTests.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; @@ -70,8 +69,8 @@ public async Task Enumerating_operations_should_be_stopped_when_error([Values(fa var subject = new CompositeWriteOperation((healthyOperation1.Object, IsMainOperation: false), (faultyOperation2.Object, IsMainOperation: false), (healthyOperation3.Object, IsMainOperation: true)); var resultedException = async - ? await Record.ExceptionAsync(() => subject.ExecuteAsync(Mock.Of(), CancellationToken.None)) - : Record.Exception(() => subject.Execute(Mock.Of(), CancellationToken.None)); + ? await Record.ExceptionAsync(() => subject.ExecuteAsync(OperationContext.NoTimeout, Mock.Of())) + : Record.Exception(() => subject.Execute(OperationContext.NoTimeout, Mock.Of())); resultedException.Should().Be(testException); @@ -93,8 +92,8 @@ public void Enumerating_operations_should_return_result_of_main_operation([Value var subject = new CompositeWriteOperation((operation1.Object, IsMainOperation: false), (operation2.Object, IsMainOperation: true), (operation3.Object, IsMainOperation: false)); var result = async - ? subject.ExecuteAsync(Mock.Of(), CancellationToken.None).GetAwaiter().GetResult() - : subject.Execute(Mock.Of(), CancellationToken.None); + ? subject.ExecuteAsync(OperationContext.NoTimeout, Mock.Of()).GetAwaiter().GetResult() + : subject.Execute(OperationContext.NoTimeout, Mock.Of()); result.Should().Be(operation2Result); @@ -108,10 +107,10 @@ private Mock> CreateFaultyOperation(Exception test { var mockedOperation = new Mock>(); mockedOperation - .Setup(c => c.Execute(It.IsAny(), It.IsAny())) + .Setup(c => c.Execute(It.IsAny(), It.IsAny())) .Throws(testException); mockedOperation - .Setup(c => c.ExecuteAsync(It.IsAny(), It.IsAny())) + .Setup(c => c.ExecuteAsync(It.IsAny(), It.IsAny())) .Throws(testException); return mockedOperation; } @@ -120,10 +119,10 @@ private Mock> CreateHealthyOperation(BsonDocument { var mockedOperation = new Mock>(); mockedOperation - .Setup(c => c.Execute(It.IsAny(), It.IsAny())) + .Setup(c => c.Execute(It.IsAny(), It.IsAny())) .Returns(response); mockedOperation - .Setup(c => c.ExecuteAsync(It.IsAny(), It.IsAny())) + .Setup(c => c.ExecuteAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(response); return mockedOperation; } @@ -132,11 +131,11 @@ private void VeryfyOperation(Mock> mockedOperation { if (async) { - mockedOperation.Verify(c => c.ExecuteAsync(It.IsAny(), It.IsAny()), hasBeenCalled ? Times.Once : Times.Never); + mockedOperation.Verify(c => c.ExecuteAsync(It.IsAny(), It.IsAny()), hasBeenCalled ? Times.Once : Times.Never); } else { - mockedOperation.Verify(c => c.Execute(It.IsAny(), It.IsAny()), hasBeenCalled ? Times.Once : Times.Never); + mockedOperation.Verify(c => c.Execute(It.IsAny(), It.IsAny()), hasBeenCalled ? Times.Once : Times.Never); } } } diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/CreateCollectionOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/CreateCollectionOperationTests.cs index 3ef1da35313..37bb5aaf6e0 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/CreateCollectionOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/CreateCollectionOperationTests.cs @@ -1031,11 +1031,11 @@ private BsonDocument ExecuteOperation(CreateCollectionOperation subject, IWriteB { if (async) { - return subject.ExecuteAsync(binding, CancellationToken.None).GetAwaiter().GetResult(); + return subject.ExecuteAsync(OperationContext.NoTimeout, binding).GetAwaiter().GetResult(); } else { - return subject.Execute(binding, CancellationToken.None); + return subject.Execute(OperationContext.NoTimeout, binding); } } @@ -1045,7 +1045,7 @@ private BsonDocument GetCollectionInfo(IReadBinding binding) { Filter = new BsonDocument("name", _collectionNamespace.CollectionName) }; - return listCollectionsOperation.Execute(binding, CancellationToken.None).Single(); + return listCollectionsOperation.Execute(OperationContext.NoTimeout, binding).Single(); } } } diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/CreateViewOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/CreateViewOperationTests.cs index ef72d20e5fa..e6edf861ed2 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/CreateViewOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/CreateViewOperationTests.cs @@ -331,7 +331,7 @@ private BsonDocument GetViewInfo(IReadBinding binding, string viewName) { Filter = new BsonDocument("name", viewName) }; - return listCollectionsOperation.Execute(binding, CancellationToken.None).Single(); + return listCollectionsOperation.Execute(OperationContext.NoTimeout, binding).Single(); } } } diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/EvalOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/EvalOperationTests.cs index 64080a96efa..b01a431d766 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/EvalOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/EvalOperationTests.cs @@ -304,11 +304,11 @@ private BsonValue ExecuteOperation(EvalOperation operation, IWriteBinding bindin { if (async) { - return operation.ExecuteAsync(binding, CancellationToken.None).GetAwaiter().GetResult(); + return operation.ExecuteAsync(OperationContext.NoTimeout, binding).GetAwaiter().GetResult(); } else { - return operation.Execute(binding, CancellationToken.None); + return operation.Execute(OperationContext.NoTimeout, binding); } } } diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/GroupOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/GroupOperationTests.cs index 4a7d7d4de0a..5bcfde3d6d8 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/GroupOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/GroupOperationTests.cs @@ -510,17 +510,7 @@ public void Execute_should_throw_when_binding_is_null( { var subject = new GroupOperation(_collectionNamespace, _key, _initial, _reduceFunction, _filter, _messageEncoderSettings); - var exception = Record.Exception(() => - { - if (async) - { - subject.ExecuteAsync(null, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - subject.Execute(null, CancellationToken.None); - } - }); + var exception = Record.Exception(() => ExecuteOperation(subject, binding: null, async)); var argumentNullException = exception.Should().BeOfType().Subject; argumentNullException.ParamName.Should().Be("binding"); diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/OperationTestBase.cs b/tests/MongoDB.Driver.Tests/Core/Operations/OperationTestBase.cs index c90c70c6132..e42b9a5a05a 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/OperationTestBase.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/OperationTestBase.cs @@ -142,7 +142,7 @@ private protected TResult ExecuteOperation(IReadOperation oper using (var binding = CreateReadBinding()) using (var bindingHandle = new ReadBindingHandle(binding)) { - return operation.Execute(bindingHandle, CancellationToken.None); + return operation.Execute(OperationContext.NoTimeout, bindingHandle); } } @@ -163,14 +163,9 @@ private protected async Task ExecuteOperationAsync(IReadOperat using (var binding = CreateReadBinding(cluster)) using (var bindingHandle = new ReadBindingHandle(binding)) { - if (async) - { - return operation.Execute(bindingHandle, CancellationToken.None); - } - else - { - return await operation.ExecuteAsync(bindingHandle, CancellationToken.None); - } + return async ? + await operation.ExecuteAsync(OperationContext.NoTimeout, bindingHandle) : + operation.Execute(OperationContext.NoTimeout, bindingHandle); } } @@ -178,11 +173,11 @@ private protected TResult ExecuteOperation(IReadOperation oper { if (async) { - return operation.ExecuteAsync(binding, CancellationToken.None).GetAwaiter().GetResult(); + return operation.ExecuteAsync(OperationContext.NoTimeout, binding).GetAwaiter().GetResult(); } else { - return operation.Execute(binding, CancellationToken.None); + return operation.Execute(OperationContext.NoTimeout, binding); } } @@ -200,7 +195,7 @@ private protected TResult ExecuteOperationSync(IWriteOperation using (var binding = CreateReadWriteBinding(useImplicitSession)) using (var bindingHandle = new ReadWriteBindingHandle(binding)) { - return operation.Execute(bindingHandle, CancellationToken.None); + return operation.Execute(OperationContext.NoTimeout, bindingHandle); } } @@ -220,11 +215,11 @@ private protected TResult ExecuteOperation(IWriteOperation ope { if (async) { - return operation.ExecuteAsync(binding, CancellationToken.None).GetAwaiter().GetResult(); + return operation.ExecuteAsync(OperationContext.NoTimeout, binding).GetAwaiter().GetResult(); } else { - return operation.Execute(binding, CancellationToken.None); + return operation.Execute(OperationContext.NoTimeout, binding); } } @@ -239,7 +234,7 @@ private protected async Task ExecuteOperationAsync(IReadOperat private protected async Task ExecuteOperationAsync(IReadOperation operation, IReadBinding binding) { - return await operation.ExecuteAsync(binding, CancellationToken.None); + return await operation.ExecuteAsync(OperationContext.NoTimeout, binding); } private protected async Task ExecuteOperationAsync(IWriteOperation operation, bool useImplicitSession = false) @@ -247,7 +242,7 @@ private protected async Task ExecuteOperationAsync(IWriteOpera using (var binding = CreateReadWriteBinding(useImplicitSession)) using (var bindingHandle = new ReadWriteBindingHandle(binding)) { - return await operation.ExecuteAsync(bindingHandle, CancellationToken.None); + return await operation.ExecuteAsync(OperationContext.NoTimeout, bindingHandle); } } @@ -258,18 +253,23 @@ private protected async Task ExecuteOperationAsync(IWriteOpera { if (async) { - return await operation.ExecuteAsync(bindingHandle, CancellationToken.None); + return await operation.ExecuteAsync(OperationContext.NoTimeout, bindingHandle); } else { - return operation.Execute(bindingHandle, CancellationToken.None); + return operation.Execute(OperationContext.NoTimeout, bindingHandle); } } } + private protected TResult ExecuteOperation(IWriteOperation operation, IWriteBinding binding, bool async) + => async ? + operation.ExecuteAsync(OperationContext.NoTimeout, binding).GetAwaiter().GetResult() : + operation.Execute(OperationContext.NoTimeout, binding); + private protected async Task ExecuteOperationAsync(IWriteOperation operation, IWriteBinding binding) { - return await operation.ExecuteAsync(binding, CancellationToken.None); + return await operation.ExecuteAsync(OperationContext.NoTimeout, binding); } private protected void CreateIndexes(params CreateIndexRequest[] requests) @@ -513,8 +513,8 @@ private protected void VerifySessionIdWasNotSentIfUnacknowledgedWrite( bool useImplicitSession) { VerifySessionIdSending( - (binding, cancellationToken) => operation.ExecuteAsync(binding, cancellationToken), - (binding, cancellationToken) => operation.Execute(binding, cancellationToken), + (binding, cancellationToken) => operation.ExecuteAsync(cancellationToken, binding), + (binding, cancellationToken) => operation.Execute(cancellationToken, binding), AssertSessionIdWasNotSentIfUnacknowledgedWrite, commandName, async, @@ -524,8 +524,8 @@ private protected void VerifySessionIdWasNotSentIfUnacknowledgedWrite( private protected void VerifySessionIdWasSentWhenSupported(IReadOperation operation, string commandName, bool async) { VerifySessionIdSending( - (binding, cancellationToken) => operation.ExecuteAsync(binding, cancellationToken), - (binding, cancellationToken) => operation.Execute(binding, cancellationToken), + (binding, cancellationToken) => operation.ExecuteAsync(cancellationToken, binding), + (binding, cancellationToken) => operation.Execute(cancellationToken, binding), AssertSessionIdWasSentWhenSupported, commandName, async); @@ -534,16 +534,16 @@ private protected void VerifySessionIdWasSentWhenSupported(IReadOperati private protected void VerifySessionIdWasSentWhenSupported(IWriteOperation operation, string commandName, bool async) { VerifySessionIdSending( - (binding, cancellationToken) => operation.ExecuteAsync(binding, cancellationToken), - (binding, cancellationToken) => operation.Execute(binding, cancellationToken), + (binding, cancellationToken) => operation.ExecuteAsync(cancellationToken, binding), + (binding, cancellationToken) => operation.Execute(cancellationToken, binding), AssertSessionIdWasSentWhenSupported, commandName, async); } private protected void VerifySessionIdSending( - Func> executeAsync, - Func execute, + Func> executeAsync, + Func execute, Action assertResults, string commandName, bool async, @@ -555,16 +555,14 @@ private protected void VerifySessionIdSending( using (var session = CreateSession(cluster, useImplicitSession)) using (var binding = new WritableServerBinding(cluster, session.Fork())) { - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; Exception exception; if (async) { - exception = Record.Exception(() => executeAsync(binding, cancellationToken).GetAwaiter().GetResult()); + exception = Record.Exception(() => executeAsync(binding, OperationContext.NoTimeout).GetAwaiter().GetResult()); } else { - exception = Record.Exception(() => execute(binding, cancellationToken)); + exception = Record.Exception(() => execute(binding, OperationContext.NoTimeout)); } assertResults(eventCapturer, session, exception); diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/ReadCommandOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/ReadCommandOperationTests.cs index e66a2fa67d1..f866f268877 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/ReadCommandOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/ReadCommandOperationTests.cs @@ -13,7 +13,6 @@ * limitations under the License. */ -using System; using System.Net; using System.Threading; using System.Threading.Tasks; @@ -33,7 +32,7 @@ namespace MongoDB.Driver.Core.Operations { - public class ReadCommandOperationTests + public class ReadCommandOperationTests : OperationTestBase { // public methods [Fact] @@ -91,14 +90,10 @@ public void Execute_should_call_channel_Command_with_unwrapped_command_when_wrap var mockChannel = CreateMockChannel(); var channelSource = CreateMockChannelSource(serverDescription, mockChannel.Object).Object; var binding = CreateMockReadBinding(readPreference, channelSource).Object; - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - BsonDocument result; + ExecuteOperation(subject, binding, async); if (async) { - result = subject.ExecuteAsync(binding, cancellationToken).GetAwaiter().GetResult(); - mockChannel.Verify( c => c.CommandAsync( binding.Session, @@ -112,13 +107,11 @@ public void Execute_should_call_channel_Command_with_unwrapped_command_when_wrap CommandResponseHandling.Return, subject.ResultSerializer, subject.MessageEncoderSettings, - cancellationToken), + It.IsAny()), Times.Once); } else { - result = subject.Execute(binding, cancellationToken); - mockChannel.Verify( c => c.Command( binding.Session, @@ -132,7 +125,7 @@ public void Execute_should_call_channel_Command_with_unwrapped_command_when_wrap CommandResponseHandling.Return, subject.ResultSerializer, subject.MessageEncoderSettings, - cancellationToken), + It.IsAny()), Times.Once); } } @@ -150,15 +143,11 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_additi var mockChannel = CreateMockChannel(); var channelSource = CreateMockChannelSource(serverDescription, mockChannel.Object).Object; var binding = CreateMockReadBinding(readPreference, channelSource).Object; - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; var additionalOptions = BsonDocument.Parse("{ $comment : \"comment\", additional : 1 }"); - BsonDocument result; + ExecuteOperation(subject, binding, async); if (async) { - result = subject.ExecuteAsync(binding, cancellationToken).GetAwaiter().GetResult(); - mockChannel.Verify( c => c.CommandAsync( binding.Session, @@ -172,13 +161,11 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_additi CommandResponseHandling.Return, subject.ResultSerializer, subject.MessageEncoderSettings, - cancellationToken), + It.IsAny()), Times.Once); } else { - result = subject.Execute(binding, cancellationToken); - mockChannel.Verify( c => c.Command( binding.Session, @@ -192,7 +179,7 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_additi CommandResponseHandling.Return, subject.ResultSerializer, subject.MessageEncoderSettings, - cancellationToken), + It.IsAny()), Times.Once); } } @@ -209,15 +196,11 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_commen var mockChannel = CreateMockChannel(); var channelSource = CreateMockChannelSource(serverDescription, mockChannel.Object).Object; var binding = CreateMockReadBinding(readPreference, channelSource).Object; - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; var additionalOptions = BsonDocument.Parse("{ $comment : \"comment\" }"); - BsonDocument result; + ExecuteOperation(subject, binding, async); if (async) { - result = subject.ExecuteAsync(binding, cancellationToken).GetAwaiter().GetResult(); - mockChannel.Verify( c => c.CommandAsync( binding.Session, @@ -231,13 +214,11 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_commen CommandResponseHandling.Return, subject.ResultSerializer, subject.MessageEncoderSettings, - cancellationToken), + It.IsAny()), Times.Once); } else { - result = subject.Execute(binding, cancellationToken); - mockChannel.Verify( c => c.Command( binding.Session, @@ -251,7 +232,7 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_commen CommandResponseHandling.Return, subject.ResultSerializer, subject.MessageEncoderSettings, - cancellationToken), + It.IsAny()), Times.Once); } } @@ -269,15 +250,11 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_readPr var mockChannel = CreateMockChannel(); var channelSource = CreateMockChannelSource(serverDescription, mockChannel.Object).Object; var binding = CreateMockReadBinding(readPreference, channelSource).Object; - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; var additionalOptions = BsonDocument.Parse("{ $comment : \"comment\", additional : 1 }"); - BsonDocument result; + ExecuteOperation(subject, binding, async); if (async) { - result = subject.ExecuteAsync(binding, cancellationToken).GetAwaiter().GetResult(); - mockChannel.Verify( c => c.CommandAsync( binding.Session, @@ -291,13 +268,11 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_readPr CommandResponseHandling.Return, subject.ResultSerializer, subject.MessageEncoderSettings, - cancellationToken), + It.IsAny()), Times.Once); } else { - result = subject.Execute(binding, cancellationToken); - mockChannel.Verify( c => c.Command( binding.Session, @@ -311,7 +286,7 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_readPr CommandResponseHandling.Return, subject.ResultSerializer, subject.MessageEncoderSettings, - cancellationToken), + It.IsAny()), Times.Once); } } @@ -326,18 +301,15 @@ public void Execute_should_call_GetChannel_only_once([Values(false, true)] bool var mockChannel = CreateMockChannel(); var mockChannelSource = CreateMockChannelSource(serverDescription, mockChannel.Object); var binding = CreateMockReadBinding(readPreference, mockChannelSource.Object).Object; - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; + ExecuteOperation(subject, binding, async); if (async) { - subject.ExecuteAsync(binding, cancellationToken).GetAwaiter().GetResult(); - mockChannelSource.Verify(c => c.GetChannelAsync(cancellationToken), Times.Once); + mockChannelSource.Verify(c => c.GetChannelAsync(It.IsAny()), Times.Once); } else { - subject.Execute(binding, cancellationToken); - mockChannelSource.Verify(c => c.GetChannel(cancellationToken), Times.Once); + mockChannelSource.Verify(c => c.GetChannel(It.IsAny()), Times.Once); } } @@ -348,8 +320,8 @@ private Mock CreateMockReadBinding(ReadPreference readPreference, var mockSession = new Mock(); mockBinding.SetupGet(b => b.ReadPreference).Returns(readPreference); mockBinding.SetupGet(b => b.Session).Returns(mockSession.Object); - mockBinding.Setup(b => b.GetReadChannelSource(It.IsAny())).Returns(channelSource); - mockBinding.Setup(b => b.GetReadChannelSourceAsync(It.IsAny())).Returns(Task.FromResult(channelSource)); + mockBinding.Setup(b => b.GetReadChannelSource(It.IsAny())).Returns(channelSource); + mockBinding.Setup(b => b.GetReadChannelSourceAsync(It.IsAny())).Returns(Task.FromResult(channelSource)); return mockBinding; } @@ -363,8 +335,8 @@ private Mock CreateMockChannelSource(ServerDescription ser { var mockChannelSource = new Mock(); mockChannelSource.SetupGet(s => s.ServerDescription).Returns(serverDescription); - mockChannelSource.Setup(s => s.GetChannel(It.IsAny())).Returns(channel); - mockChannelSource.Setup(s => s.GetChannelAsync(It.IsAny())).Returns(Task.FromResult(channel)); + mockChannelSource.Setup(s => s.GetChannel(It.IsAny())).Returns(channel); + mockChannelSource.Setup(s => s.GetChannelAsync(It.IsAny())).Returns(Task.FromResult(channel)); return mockChannelSource; } diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/RetryableWriteOperationExecutorTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/RetryableWriteOperationExecutorTests.cs index 9dd51a1ec42..471c4a0a931 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/RetryableWriteOperationExecutorTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/RetryableWriteOperationExecutorTests.cs @@ -98,7 +98,7 @@ private IWriteBinding CreateBinding(bool areRetryableWritesSupported, bool hasSe var session = CreateSession(hasSessionId, isInTransaction); var channelSource = CreateChannelSource(areRetryableWritesSupported); mockBinding.SetupGet(m => m.Session).Returns(session); - mockBinding.Setup(m => m.GetWriteChannelSource(CancellationToken.None)).Returns(channelSource); + mockBinding.Setup(m => m.GetWriteChannelSource(It.IsAny())).Returns(channelSource); return mockBinding.Object; } @@ -114,7 +114,7 @@ private IChannelSourceHandle CreateChannelSource(bool areRetryableWritesSupporte { var mockChannelSource = new Mock(); var channel = CreateChannel(areRetryableWritesSupported); - mockChannelSource.Setup(m => m.GetChannel(CancellationToken.None)).Returns(channel); + mockChannelSource.Setup(m => m.GetChannel(It.IsAny())).Returns(channel); return mockChannelSource.Object; } @@ -142,7 +142,7 @@ private ConnectionDescription CreateConnectionDescription(bool withLogicalSessio private RetryableWriteContext CreateContext(bool retryRequested, bool areRetryableWritesSupported, bool hasSessionId, bool isInTransaction) { var binding = CreateBinding(areRetryableWritesSupported, hasSessionId, isInTransaction); - return RetryableWriteContext.Create(binding, retryRequested, CancellationToken.None); + return RetryableWriteContext.Create(OperationContext.NoTimeout, binding, retryRequested); } private IRetryableWriteOperation CreateOperation(bool withWriteConcern, bool isAcknowledged) diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/WriteCommandOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/WriteCommandOperationTests.cs index 6da9926706f..bc44255fa2c 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/WriteCommandOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/WriteCommandOperationTests.cs @@ -13,7 +13,6 @@ * limitations under the License. */ -using System; using System.Net; using System.Threading; using System.Threading.Tasks; @@ -33,7 +32,7 @@ namespace MongoDB.Driver.Core.Operations { - public class WriteCommandOperationTests + public class WriteCommandOperationTests : OperationTestBase { // public methods [Fact] @@ -65,14 +64,10 @@ public void Execute_should_call_channel_Command_with_unwrapped_command_when_wrap var mockChannel = CreateMockChannel(); var channelSource = CreateMockChannelSource(serverDescription, mockChannel.Object).Object; var binding = CreateMockWriteBinding(channelSource).Object; - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; - BsonDocument result; + ExecuteOperation(subject, binding, async); if (async) { - result = subject.ExecuteAsync(binding, cancellationToken).GetAwaiter().GetResult(); - mockChannel.Verify( c => c.CommandAsync( binding.Session, @@ -86,13 +81,11 @@ public void Execute_should_call_channel_Command_with_unwrapped_command_when_wrap CommandResponseHandling.Return, subject.ResultSerializer, subject.MessageEncoderSettings, - cancellationToken), + It.IsAny()), Times.Once); } else { - result = subject.Execute(binding, cancellationToken); - mockChannel.Verify( c => c.Command( binding.Session, @@ -106,7 +99,7 @@ public void Execute_should_call_channel_Command_with_unwrapped_command_when_wrap CommandResponseHandling.Return, subject.ResultSerializer, subject.MessageEncoderSettings, - cancellationToken), + It.IsAny()), Times.Once); } } @@ -122,15 +115,11 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_additi var mockChannel = CreateMockChannel(); var channelSource = CreateMockChannelSource(serverDescription, mockChannel.Object).Object; var binding = CreateMockWriteBinding(channelSource).Object; - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; var command = BsonDocument.Parse("{ command : 1 }"); - BsonDocument result; + ExecuteOperation(subject, binding, async); if (async) { - result = subject.ExecuteAsync(binding, cancellationToken).GetAwaiter().GetResult(); - mockChannel.Verify( c => c.CommandAsync( It.IsAny(), @@ -144,13 +133,11 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_additi CommandResponseHandling.Return, subject.ResultSerializer, subject.MessageEncoderSettings, - cancellationToken), + It.IsAny()), Times.Once); } else { - result = subject.Execute(binding, cancellationToken); - mockChannel.Verify( c => c.Command( It.IsAny(), @@ -164,7 +151,7 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_additi CommandResponseHandling.Return, subject.ResultSerializer, subject.MessageEncoderSettings, - cancellationToken), + It.IsAny()), Times.Once); } } @@ -180,15 +167,11 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_commen var mockChannel = CreateMockChannel(); var channelSource = CreateMockChannelSource(serverDescription, mockChannel.Object).Object; var binding = CreateMockWriteBinding(channelSource).Object; - using var cancellationTokenSource = new CancellationTokenSource(); - var cancellationToken = cancellationTokenSource.Token; var additionalOptions = BsonDocument.Parse("{ $comment : \"comment\" }"); - BsonDocument result; + ExecuteOperation(subject, binding, async); if (async) { - result = subject.ExecuteAsync(binding, cancellationToken).GetAwaiter().GetResult(); - mockChannel.Verify( c => c.CommandAsync( binding.Session, @@ -202,13 +185,11 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_commen CommandResponseHandling.Return, subject.ResultSerializer, subject.MessageEncoderSettings, - cancellationToken), + It.IsAny()), Times.Once); } else { - result = subject.Execute(binding, cancellationToken); - mockChannel.Verify( c => c.Command( binding.Session, @@ -222,7 +203,7 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_commen CommandResponseHandling.Return, subject.ResultSerializer, subject.MessageEncoderSettings, - cancellationToken), + It.IsAny()), Times.Once); } } @@ -233,8 +214,8 @@ private Mock CreateMockWriteBinding(IChannelSourceHandle channelS var mockBinding = new Mock(); var mockSession = new Mock(); mockBinding.SetupGet(b => b.Session).Returns(mockSession.Object); - mockBinding.Setup(b => b.GetWriteChannelSource(It.IsAny())).Returns(channelSource); - mockBinding.Setup(b => b.GetWriteChannelSourceAsync(It.IsAny())).Returns(Task.FromResult(channelSource)); + mockBinding.Setup(b => b.GetWriteChannelSource(It.IsAny())).Returns(channelSource); + mockBinding.Setup(b => b.GetWriteChannelSourceAsync(It.IsAny())).Returns(Task.FromResult(channelSource)); return mockBinding; } @@ -248,8 +229,8 @@ private Mock CreateMockChannelSource(ServerDescription ser { var mockChannelSource = new Mock(); mockChannelSource.SetupGet(s => s.ServerDescription).Returns(serverDescription); - mockChannelSource.Setup(s => s.GetChannel(It.IsAny())).Returns(channel); - mockChannelSource.Setup(s => s.GetChannelAsync(It.IsAny())).Returns(Task.FromResult(channel)); + mockChannelSource.Setup(s => s.GetChannel(It.IsAny())).Returns(channel); + mockChannelSource.Setup(s => s.GetChannelAsync(It.IsAny())).Returns(Task.FromResult(channel)); return mockChannelSource; } diff --git a/tests/MongoDB.Driver.Tests/Core/Servers/LoadBalancedServerTests.cs b/tests/MongoDB.Driver.Tests/Core/Servers/LoadBalancedServerTests.cs index 6a663956ebe..16709b65bf3 100644 --- a/tests/MongoDB.Driver.Tests/Core/Servers/LoadBalancedServerTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Servers/LoadBalancedServerTests.cs @@ -57,12 +57,10 @@ public LoadBalancedTests(ITestOutputHelper output) : base(output) _clusterId = new ClusterId(); _endPoint = new DnsEndPoint("localhost", 27017); - var mockConnectionExceptionHandler = new Mock(); - _clusterClock = new Mock().Object; _mockConnectionPool = new Mock(); - _mockConnectionPool.Setup(p => p.AcquireConnection(It.IsAny())).Returns(new Mock().Object); - _mockConnectionPool.Setup(p => p.AcquireConnectionAsync(It.IsAny())).Returns(Task.FromResult(new Mock().Object)); + _mockConnectionPool.Setup(p => p.AcquireConnection(It.IsAny())).Returns(new Mock().Object); + _mockConnectionPool.Setup(p => p.AcquireConnectionAsync(It.IsAny())).Returns(Task.FromResult(new Mock().Object)); _mockConnectionPoolFactory = new Mock(); _mockConnectionPoolFactory .Setup(f => f.CreateConnectionPool(It.IsAny(), _endPoint, It.IsAny())) @@ -79,13 +77,13 @@ public LoadBalancedTests(ITestOutputHelper output) : base(output) [Theory] [ParameterAttributeData] - public void ChannelFork_should_not_affect_operations_count([Values(false, true)] bool async) + public async Task ChannelFork_should_not_affect_operations_count([Values(false, true)] bool async) { IClusterableServer server = SetupServer(false, false); var channel = async ? - server.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult() : - server.GetChannel(CancellationToken.None); + await server.GetChannelAsync(OperationContext.NoTimeout) : + server.GetChannel(OperationContext.NoTimeout); server.OutstandingOperationsCount.Should().Be(1); @@ -169,7 +167,7 @@ public void Dispose_should_dispose_the_server() [Theory] [ParameterAttributeData] - public void GetChannel_should_clear_connection_pool_when_opening_connection_throws_MongoAuthenticationException( + public async Task GetChannel_should_clear_connection_pool_when_opening_connection_throws_MongoAuthenticationException( [Values(false, true)] bool async) { var connectionId = new ConnectionId(new ServerId(_clusterId, _endPoint)); @@ -181,11 +179,11 @@ public void GetChannel_should_clear_connection_pool_when_opening_connection_thro var mockConnectionPool = new Mock(); var authenticationException = new MongoAuthenticationException(connectionId, "Invalid login.") { ServiceId = ObjectId.GenerateNewId() }; mockConnectionPool - .Setup(p => p.AcquireConnection(It.IsAny())) + .Setup(p => p.AcquireConnection(It.IsAny())) .Callback(() => server.HandleExceptionOnOpen(authenticationException)) .Throws(authenticationException); mockConnectionPool - .Setup(p => p.AcquireConnectionAsync(It.IsAny())) + .Setup(p => p.AcquireConnectionAsync(It.IsAny())) .Callback(() => server.HandleExceptionOnOpen(authenticationException)) .Throws(authenticationException); mockConnectionPool.Setup(p => p.Clear(It.IsAny())); @@ -205,17 +203,9 @@ public void GetChannel_should_clear_connection_pool_when_opening_connection_thro _eventLogger); server.Initialize(); - var exception = Record.Exception(() => - { - if (async) - { - server.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - server.GetChannel(CancellationToken.None); - } - }); + var exception = async ? + await Record.ExceptionAsync(() => server.GetChannelAsync(OperationContext.NoTimeout)) : + Record.Exception(() => server.GetChannel(OperationContext.NoTimeout)); exception.Should().BeOfType(); mockConnectionPool.Verify(p => p.Clear(It.IsAny()), Times.Once()); @@ -223,42 +213,28 @@ public void GetChannel_should_clear_connection_pool_when_opening_connection_thro [Theory] [ParameterAttributeData] - public void GetChannel_should_get_a_connection([Values(false, true)] bool async) + public async Task GetChannel_should_get_a_connection([Values(false, true)] bool async) { _subject.Initialize(); - IChannelHandle channel; - if (async) - { - channel = _subject.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - channel = _subject.GetChannel(CancellationToken.None); - } + var channel = async ? + await _subject.GetChannelAsync(OperationContext.NoTimeout) : + _subject.GetChannel(OperationContext.NoTimeout); channel.Should().NotBeNull(); } [Theory] [ParameterAttributeData] - public void GetChannel_should_not_increase_operations_count_on_exception( + public async Task GetChannel_should_not_increase_operations_count_on_exception( [Values(false, true)] bool async, [Values(false, true)] bool connectionOpenException) { IClusterableServer server = SetupServer(connectionOpenException, !connectionOpenException); - var exception = Record.Exception(() => - { - if (async) - { - server.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - server.GetChannel(CancellationToken.None); - } - }); + var exception = async ? + await Record.ExceptionAsync(() => _subject.GetChannelAsync(OperationContext.NoTimeout)) : + Record.Exception(() => _subject.GetChannel(OperationContext.NoTimeout)); exception.Should().NotBeNull(); server.OutstandingOperationsCount.Should().Be(0); @@ -266,7 +242,7 @@ public void GetChannel_should_not_increase_operations_count_on_exception( [Theory] [ParameterAttributeData] - public void GetChannel_should_set_operations_count_correctly( + public async Task GetChannel_should_set_operations_count_correctly( [Values(false, true)] bool async, [Values(0, 1, 2, 10)] int operationsCount) { @@ -275,14 +251,10 @@ public void GetChannel_should_set_operations_count_correctly( var channels = new List(); for (int i = 0; i < operationsCount; i++) { - if (async) - { - channels.Add(server.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult()); - } - else - { - channels.Add(server.GetChannel(CancellationToken.None)); - } + var channel = async ? + await server.GetChannelAsync(OperationContext.NoTimeout) : + server.GetChannel(OperationContext.NoTimeout); + channels.Add(channel); } server.OutstandingOperationsCount.Should().Be(operationsCount); @@ -296,44 +268,32 @@ public void GetChannel_should_set_operations_count_correctly( [Theory] [ParameterAttributeData] - public void GetChannel_should_throw_when_not_initialized( + public async Task GetChannel_should_throw_when_not_initialized( [Values(false, true)] bool async) { - Exception exception; - if (async) - { - exception = Record.Exception(() => _subject.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult()); - } - else - { - exception = Record.Exception(() => _subject.GetChannel(CancellationToken.None)); - } + var exception = async ? + await Record.ExceptionAsync(() => _subject.GetChannelAsync(OperationContext.NoTimeout)) : + Record.Exception(() => _subject.GetChannel(OperationContext.NoTimeout)); exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void GetChannel_should_throw_when_disposed([Values(false, true)] bool async) + public async Task GetChannel_should_throw_when_disposed([Values(false, true)] bool async) { _subject.Dispose(); - Exception exception; - if (async) - { - exception = Record.Exception(() => _subject.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult()); - } - else - { - exception = Record.Exception(() => _subject.GetChannel(CancellationToken.None)); - } + var exception = async ? + await Record.ExceptionAsync(() => _subject.GetChannelAsync(OperationContext.NoTimeout)) : + Record.Exception(() => _subject.GetChannel(OperationContext.NoTimeout)); exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void GetChannel_should_not_update_topology_and_clear_connection_pool_on_MongoConnectionException( + public async Task GetChannel_should_not_update_topology_and_clear_connection_pool_on_MongoConnectionException( [Values("TimedOutSocketException", "NetworkUnreachableSocketException")] string errorType, [Values(false, true)] bool async) { @@ -363,18 +323,10 @@ public void GetChannel_should_not_update_topology_and_clear_connection_pool_on_M var subject = new LoadBalancedServer(_clusterId, _clusterClock, _settings, _endPoint, mockConnectionPoolFactory.Object, _serverApi, _eventLogger); subject.Initialize(); - IChannelHandle channel = null; - Exception exception; - if (async) - { - exception = Record.Exception(() => channel = subject.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult()); - } - else - { - exception = Record.Exception(() => channel = subject.GetChannel(CancellationToken.None)); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.GetChannelAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetChannel(OperationContext.NoTimeout)); - channel.Should().BeNull(); exception.Should().Be(openConnectionException); subject.Description.Type.Should().Be(ServerType.LoadBalanced); subject.Description.ReasonChanged.Should().Be("Initialized"); @@ -434,30 +386,30 @@ private Server SetupServer(bool exceptionOnConnectionOpen, bool exceptionOnConne if (exceptionOnConnectionAcquire) { mockConnectionPool - .Setup(p => p.AcquireConnection(It.IsAny())) + .Setup(p => p.AcquireConnection(It.IsAny())) .Throws(new TimeoutException("Timeout")); mockConnectionPool - .Setup(p => p.AcquireConnectionAsync(It.IsAny())) + .Setup(p => p.AcquireConnectionAsync(It.IsAny())) .Throws(new TimeoutException("Timeout")); mockConnectionPool.Setup(p => p.Clear(It.IsAny())); } else if (exceptionOnConnectionOpen) { mockConnectionPool - .Setup(p => p.AcquireConnection(It.IsAny())) + .Setup(p => p.AcquireConnection(It.IsAny())) .Throws(new MongoAuthenticationException(connectionId, "Invalid login.")); mockConnectionPool - .Setup(p => p.AcquireConnectionAsync(It.IsAny())) + .Setup(p => p.AcquireConnectionAsync(It.IsAny())) .Throws(new MongoAuthenticationException(connectionId, "Invalid login.")); mockConnectionPool.Setup(p => p.Clear(It.IsAny())); } else { mockConnectionPool - .Setup(p => p.AcquireConnection(It.IsAny())) + .Setup(p => p.AcquireConnection(It.IsAny())) .Returns(mockConnectionHandle.Object); mockConnectionPool - .Setup(p => p.AcquireConnectionAsync(It.IsAny())) + .Setup(p => p.AcquireConnectionAsync(It.IsAny())) .Returns(Task.FromResult(mockConnectionHandle.Object)); mockConnectionPool.Setup(p => p.Clear(It.IsAny())); } diff --git a/tests/MongoDB.Driver.Tests/Core/Servers/ServerTests.cs b/tests/MongoDB.Driver.Tests/Core/Servers/ServerTests.cs index d0c56e46227..cd564ab9773 100644 --- a/tests/MongoDB.Driver.Tests/Core/Servers/ServerTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Servers/ServerTests.cs @@ -71,8 +71,8 @@ public ServerTests(ITestOutputHelper output) : base(output) _clusterClock = new Mock().Object; _directConnection = false; _mockConnectionPool = new Mock(); - _mockConnectionPool.Setup(p => p.AcquireConnection(It.IsAny())).Returns(new Mock().Object); - _mockConnectionPool.Setup(p => p.AcquireConnectionAsync(It.IsAny())).Returns(Task.FromResult(new Mock().Object)); + _mockConnectionPool.Setup(p => p.AcquireConnection(It.IsAny())).Returns(new Mock().Object); + _mockConnectionPool.Setup(p => p.AcquireConnectionAsync(It.IsAny())).Returns(Task.FromResult(new Mock().Object)); _mockConnectionPoolFactory = new Mock(); _mockConnectionPoolFactory .Setup(f => f.CreateConnectionPool(It.IsAny(), _endPoint, It.IsAny())) @@ -99,13 +99,13 @@ protected override void DisposeInternal() [Theory] [ParameterAttributeData] - public void ChannelFork_should_not_affect_operations_count([Values(false, true)] bool async) + public async Task ChannelFork_should_not_affect_operations_count([Values(false, true)] bool async) { IClusterableServer server = SetupServer(false, false); var channel = async ? - server.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult() : - server.GetChannel(CancellationToken.None); + await server.GetChannelAsync(OperationContext.NoTimeout) : + server.GetChannel(OperationContext.NoTimeout); server.OutstandingOperationsCount.Should().Be(1); @@ -200,12 +200,10 @@ public void Dispose_should_dispose_the_server() [Theory] [ParameterAttributeData] - public void GetChannel_should_clear_connection_pool_when_opening_connection_throws_MongoAuthenticationException( + public async Task GetChannel_should_clear_connection_pool_when_opening_connection_throws_MongoAuthenticationException( [Values(false, true)] bool async) { var connectionId = new ConnectionId(new ServerId(_clusterId, _endPoint)); - var mockConnectionHandle = new Mock(); - var mockConnectionPool = new Mock(); var mockConnectionPoolFactory = new Mock(); @@ -226,28 +224,20 @@ public void GetChannel_should_clear_connection_pool_when_opening_connection_thro var exceptionToThrow = new MongoAuthenticationException(connectionId, "Invalid login."); mockConnectionPool - .Setup(p => p.AcquireConnection(It.IsAny())) + .Setup(p => p.AcquireConnection(It.IsAny())) .Callback(() => server.HandleExceptionOnOpen(exceptionToThrow)) .Throws(exceptionToThrow); mockConnectionPool - .Setup(p => p.AcquireConnectionAsync(It.IsAny())) + .Setup(p => p.AcquireConnectionAsync(It.IsAny())) .Callback(() => server.HandleExceptionOnOpen(exceptionToThrow)) .Throws(exceptionToThrow); mockConnectionPool.Setup(p => p.Clear(It.IsAny())); server.Initialize(); - var exception = Record.Exception(() => - { - if (async) - { - server.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - server.GetChannel(CancellationToken.None); - } - }); + var exception = async ? + await Record.ExceptionAsync(() => server.GetChannelAsync(OperationContext.NoTimeout)) : + Record.Exception(() => server.GetChannel(OperationContext.NoTimeout)); exception.Should().BeOfType(); mockConnectionPool.Verify(p => p.Clear(It.IsAny()), Times.Once()); @@ -255,51 +245,38 @@ public void GetChannel_should_clear_connection_pool_when_opening_connection_thro [Theory] [ParameterAttributeData] - public void GetChannel_should_get_a_connection( + public async Task GetChannel_should_get_a_connection( [Values(false, true)] bool async) { _subject.Initialize(); - IChannelHandle channel; - if (async) - { - channel = _subject.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - channel = _subject.GetChannel(CancellationToken.None); - } + var channel = async ? + await _subject.GetChannelAsync(OperationContext.NoTimeout) : + _subject.GetChannel(OperationContext.NoTimeout); channel.Should().NotBeNull(); } [Theory] [ParameterAttributeData] - public void GetChannel_should_not_increase_operations_count_on_exception( + public async Task GetChannel_should_not_increase_operations_count_on_exception( [Values(false, true)] bool async, [Values(false, true)] bool connectionOpenException) { IClusterableServer server = SetupServer(connectionOpenException, !connectionOpenException); - _ = Record.Exception(() => - { - if (async) - { - server.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - server.GetChannel(CancellationToken.None); - } - }); + var exception = async ? + await Record.ExceptionAsync(() => _subject.GetChannelAsync(OperationContext.NoTimeout)) : + Record.Exception(() => _subject.GetChannel(OperationContext.NoTimeout)); + exception.Should().NotBeNull(); server.OutstandingOperationsCount.Should().Be(0); } [Theory] [ParameterAttributeData] - public void GetChannel_should_set_operations_count_correctly( + public async Task GetChannel_should_set_operations_count_correctly( [Values(false, true)] bool async, [Values(0, 1, 2, 10)] int operationsCount) { @@ -308,14 +285,10 @@ public void GetChannel_should_set_operations_count_correctly( var channels = new List(); for (int i = 0; i < operationsCount; i++) { - if (async) - { - channels.Add(server.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult()); - } - else - { - channels.Add(server.GetChannel(CancellationToken.None)); - } + var channel = async ? + await server.GetChannelAsync(OperationContext.NoTimeout) : + server.GetChannel(OperationContext.NoTimeout); + channels.Add(channel); } server.OutstandingOperationsCount.Should().Be(operationsCount); @@ -329,46 +302,34 @@ public void GetChannel_should_set_operations_count_correctly( [Theory] [ParameterAttributeData] - public void GetChannel_should_throw_when_not_initialized( + public async Task GetChannel_should_throw_when_not_initialized( [Values(false, true)] bool async) { - Action act; - if (async) - { - act = () => _subject.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => _subject.GetChannel(CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => _subject.GetChannelAsync(OperationContext.NoTimeout)) : + Record.Exception(() => _subject.GetChannel(OperationContext.NoTimeout)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void GetChannel_should_throw_when_disposed( + public async Task GetChannel_should_throw_when_disposed( [Values(false, true)] bool async) { _subject.Dispose(); - Action act; - if (async) - { - act = () => _subject.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => _subject.GetChannel(CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => _subject.GetChannelAsync(OperationContext.NoTimeout)) : + Record.Exception(() => _subject.GetChannel(OperationContext.NoTimeout)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void GetChannel_should_update_topology_and_clear_connection_pool_on_network_error_or_timeout( + public async Task GetChannel_should_update_topology_and_clear_connection_pool_on_network_error_or_timeout( [Values("TimedOutSocketException", "NetworkUnreachableSocketException")] string errorType, [Values(false, true)] bool async) { @@ -406,18 +367,10 @@ public void GetChannel_should_update_topology_and_clear_connection_pool_on_netwo subject.Initialize(); connectionPool.SetReady(); - IChannelHandle channel = null; - Exception exception; - if (async) - { - exception = Record.Exception(() => channel = subject.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult()); - } - else - { - exception = Record.Exception(() => channel = subject.GetChannel(CancellationToken.None)); - } + var exception = async ? + await Record.ExceptionAsync(() => subject.GetChannelAsync(OperationContext.NoTimeout)) : + Record.Exception(() => subject.GetChannel(OperationContext.NoTimeout)); - channel.Should().BeNull(); exception.Should().Be(openConnectionException); subject.Description.Type.Should().Be(ServerType.Unknown); subject.Description.ReasonChanged.Should().Contain("ChannelException during handshake"); @@ -449,8 +402,8 @@ public void HandleChannelException_should_update_topology_as_expected_on_network mockConnection.SetupGet(c => c.Description) .Returns(new ConnectionDescription(new ConnectionId(serverId, 0), helloResult)); var mockConnectionPool = new Mock(); - mockConnectionPool.Setup(p => p.AcquireConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnectionPool.Setup(p => p.AcquireConnectionAsync(It.IsAny())).ReturnsAsync(mockConnection.Object); + mockConnectionPool.Setup(p => p.AcquireConnection(It.IsAny())).Returns(mockConnection.Object); + mockConnectionPool.Setup(p => p.AcquireConnectionAsync(It.IsAny())).ReturnsAsync(mockConnection.Object); var mockConnectionPoolFactory = new Mock(); mockConnectionPoolFactory .Setup(f => f.CreateConnectionPool(It.IsAny(), _endPoint, It.IsAny())) @@ -834,30 +787,30 @@ private Server SetupServer(bool exceptionOnConnectionOpen, bool exceptionOnConne if (exceptionOnConnectionAcquire) { mockConnectionPool - .Setup(p => p.AcquireConnection(It.IsAny())) + .Setup(p => p.AcquireConnection(It.IsAny())) .Throws(new TimeoutException("Timeout")); mockConnectionPool - .Setup(p => p.AcquireConnectionAsync(It.IsAny())) + .Setup(p => p.AcquireConnectionAsync(It.IsAny())) .Throws(new TimeoutException("Timeout")); mockConnectionPool.Setup(p => p.Clear(It.IsAny())); } else if (exceptionOnConnectionOpen) { mockConnectionPool - .Setup(p => p.AcquireConnection(It.IsAny())) + .Setup(p => p.AcquireConnection(It.IsAny())) .Throws(new MongoAuthenticationException(connectionId, "Invalid login.")); mockConnectionPool - .Setup(p => p.AcquireConnectionAsync(It.IsAny())) + .Setup(p => p.AcquireConnectionAsync(It.IsAny())) .Throws(new MongoAuthenticationException(connectionId, "Invalid login.")); mockConnectionPool.Setup(p => p.Clear(It.IsAny())); } else { mockConnectionPool - .Setup(p => p.AcquireConnection(It.IsAny())) + .Setup(p => p.AcquireConnection(It.IsAny())) .Returns(mockConnectionHandle.Object); mockConnectionPool - .Setup(p => p.AcquireConnectionAsync(It.IsAny())) + .Setup(p => p.AcquireConnectionAsync(It.IsAny())) .Returns(Task.FromResult(mockConnectionHandle.Object)); mockConnectionPool.Setup(p => p.Clear(It.IsAny())); } @@ -900,9 +853,8 @@ public void Command_should_send_the_greater_of_the_session_and_cluster_cluster_t using (var cluster = CoreTestConfiguration.CreateCluster(b => b.Subscribe(eventCapturer))) using (var session = cluster.StartSession()) { - var cancellationToken = CancellationToken.None; - var server = (Server)cluster.SelectServer(WritableServerSelector.Instance, cancellationToken); - using (var channel = server.GetChannel(cancellationToken)) + var server = (Server)cluster.SelectServer(OperationContext.NoTimeout, WritableServerSelector.Instance); + using (var channel = server.GetChannel(OperationContext.NoTimeout)) { session.AdvanceClusterTime(sessionClusterTime); server.ClusterClock.AdvanceClusterTime(clusterClusterTime); @@ -922,7 +874,7 @@ public void Command_should_send_the_greater_of_the_session_and_cluster_cluster_t CommandResponseHandling.Return, BsonDocumentSerializer.Instance, new MessageEncoderSettings(), - cancellationToken); + It.IsAny()); } catch (MongoCommandException ex) { @@ -948,9 +900,8 @@ public void Command_should_update_the_session_and_cluster_cluster_times() using (var cluster = CoreTestConfiguration.CreateCluster(b => b.Subscribe(eventCapturer))) using (var session = cluster.StartSession()) { - var cancellationToken = CancellationToken.None; - var server = (Server)cluster.SelectServer(WritableServerSelector.Instance, cancellationToken); - using (var channel = server.GetChannel(cancellationToken)) + var server = (Server)cluster.SelectServer(OperationContext.NoTimeout, WritableServerSelector.Instance); + using (var channel = server.GetChannel(OperationContext.NoTimeout)) { var command = BsonDocument.Parse("{ ping : 1 }"); channel.Command( @@ -965,7 +916,7 @@ public void Command_should_update_the_session_and_cluster_cluster_times() CommandResponseHandling.Return, BsonDocumentSerializer.Instance, new MessageEncoderSettings(), - cancellationToken); + It.IsAny()); } var commandSucceededEvent = eventCapturer.Next().Should().BeOfType().Subject; @@ -978,7 +929,7 @@ public void Command_should_update_the_session_and_cluster_cluster_times() [Theory] [ParameterAttributeData] - public void Command_should_use_serverApi([Values(false, true)] bool async) + public async Task Command_should_use_serverApi([Values(false, true)] bool async) { RequireServer.Check(); @@ -992,14 +943,13 @@ public void Command_should_use_serverApi([Values(false, true)] bool async) using (var cluster = CoreTestConfiguration.CreateCluster(builder)) using (var session = cluster.StartSession()) { - var cancellationToken = CancellationToken.None; - var server = (Server)cluster.SelectServer(WritableServerSelector.Instance, cancellationToken); - using (var channel = server.GetChannel(cancellationToken)) + var server = (Server)cluster.SelectServer(OperationContext.NoTimeout, WritableServerSelector.Instance); + using (var channel = server.GetChannel(OperationContext.NoTimeout)) { var command = BsonDocument.Parse("{ ping : 1 }"); if (async) { - channel + await channel .CommandAsync( session, ReadPreference.Primary, @@ -1012,9 +962,7 @@ public void Command_should_use_serverApi([Values(false, true)] bool async) CommandResponseHandling.Return, BsonDocumentSerializer.Instance, new MessageEncoderSettings(), - cancellationToken) - .GetAwaiter() - .GetResult(); + It.IsAny()); } else { @@ -1030,7 +978,7 @@ public void Command_should_use_serverApi([Values(false, true)] bool async) CommandResponseHandling.Return, BsonDocumentSerializer.Instance, new MessageEncoderSettings(), - cancellationToken); + It.IsAny()); } } } diff --git a/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs b/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs index 69cb78ed104..fb09f34e818 100644 --- a/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs +++ b/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs @@ -137,14 +137,14 @@ public async Task CreateEncryptedCollection_should_handle_generated_key_when_sec var mockServer = new Mock(); mockServer.SetupGet(s => s.Description).Returns(serverDescription); var channel = Mock.Of(c => c.ConnectionDescription == new ConnectionDescription(new ConnectionId(serverId), new HelloResult(new BsonDocument("maxWireVersion", serverDescription.WireVersionRange.Max)))); - mockServer.Setup(s => s.GetChannel(It.IsAny())).Returns(channel); - mockServer.Setup(s => s.GetChannelAsync(It.IsAny())).ReturnsAsync(channel); + mockServer.Setup(s => s.GetChannel(It.IsAny())).Returns(channel); + mockServer.Setup(s => s.GetChannelAsync(It.IsAny())).ReturnsAsync(channel); mockCluster - .Setup(m => m.SelectServer(It.IsAny(), It.IsAny())) + .Setup(m => m.SelectServer(It.IsAny(), It.IsAny())) .Returns(mockServer.Object); mockCluster - .Setup(m => m.SelectServerAsync(It.IsAny(), It.IsAny())) + .Setup(m => m.SelectServerAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(mockServer.Object); var database = Mock.Of(d => @@ -226,14 +226,14 @@ public async Task CreateEncryptedCollection_should_handle_various_encryptedField var mockServer = new Mock(); mockServer.SetupGet(s => s.Description).Returns(serverDescription); var channel = Mock.Of(c => c.ConnectionDescription == new ConnectionDescription(new ConnectionId(serverId), new HelloResult(new BsonDocument("maxWireVersion", serverDescription.WireVersionRange.Max)))); - mockServer.Setup(s => s.GetChannel(It.IsAny())).Returns(channel); - mockServer.Setup(s => s.GetChannelAsync(It.IsAny())).ReturnsAsync(channel); + mockServer.Setup(s => s.GetChannel(It.IsAny())).Returns(channel); + mockServer.Setup(s => s.GetChannelAsync(It.IsAny())).ReturnsAsync(channel); mockCluster - .Setup(m => m.SelectServer(It.IsAny(), It.IsAny())) + .Setup(m => m.SelectServer(It.IsAny(), It.IsAny())) .Returns(mockServer.Object); mockCluster - .Setup(m => m.SelectServerAsync(It.IsAny(), It.IsAny())) + .Setup(m => m.SelectServerAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(mockServer.Object); var database = Mock.Of(d => diff --git a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenConfigureFailPointTest.cs b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenConfigureFailPointTest.cs index b3720607b1c..389449d592d 100644 --- a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenConfigureFailPointTest.cs +++ b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenConfigureFailPointTest.cs @@ -52,7 +52,7 @@ protected virtual IServer GetFailPointServer() } var cluster = TestRunner.FailPointCluster; - return cluster.SelectServer(WritableServerSelector.Instance, CancellationToken.None); + return cluster.SelectServer(OperationContext.NoTimeout, WritableServerSelector.Instance); } protected async virtual Task GetFailPointServerAsync() @@ -63,7 +63,7 @@ protected async virtual Task GetFailPointServerAsync() } var cluster = TestRunner.FailPointCluster; - return await cluster.SelectServerAsync(WritableServerSelector.Instance, CancellationToken.None).ConfigureAwait(false); + return await cluster.SelectServerAsync(OperationContext.NoTimeout, WritableServerSelector.Instance).ConfigureAwait(false); } protected override void SetArgument(string name, BsonValue value) diff --git a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTargetedFailPointTest.cs b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTargetedFailPointTest.cs index 44f2e342395..8a34e83638d 100644 --- a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTargetedFailPointTest.cs +++ b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTargetedFailPointTest.cs @@ -15,7 +15,6 @@ using System.Collections.Generic; using System.Net; -using System.Threading; using System.Threading.Tasks; using FluentAssertions; using MongoDB.Driver.Core.Clusters.ServerSelectors; @@ -34,17 +33,17 @@ protected override IServer GetFailPointServer() { var pinnedServerEndpoint = GetPinnedServerEndpointAndAssertNotNull(); var pinnedServerSelector = CreateServerSelector(pinnedServerEndpoint); - return TestRunner.FailPointCluster.SelectServer(pinnedServerSelector, CancellationToken.None); + return TestRunner.FailPointCluster.SelectServer(OperationContext.NoTimeout, pinnedServerSelector); } protected async override Task GetFailPointServerAsync() { var pinnedServerEndpoint = GetPinnedServerEndpointAndAssertNotNull(); var pinnedServerSelector = CreateServerSelector(pinnedServerEndpoint); - return await TestRunner.FailPointCluster.SelectServerAsync(pinnedServerSelector, CancellationToken.None).ConfigureAwait(false); + return await TestRunner.FailPointCluster.SelectServerAsync(OperationContext.NoTimeout, pinnedServerSelector).ConfigureAwait(false); } - // private methods + // private methods private IServerSelector CreateServerSelector(EndPoint endpoint) { return new CompositeServerSelector(new IServerSelector[] diff --git a/tests/MongoDB.Driver.Tests/OperationContextTests.cs b/tests/MongoDB.Driver.Tests/OperationContextTests.cs new file mode 100644 index 00000000000..9b253837d8a --- /dev/null +++ b/tests/MongoDB.Driver.Tests/OperationContextTests.cs @@ -0,0 +1,281 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; + +namespace MongoDB.Driver.Tests +{ + public class OperationContextTests + { + [Fact] + public void Constructor_should_initialize_properties() + { + var timeout = TimeSpan.FromSeconds(42); + var stopwatch = new Stopwatch(); + using var cancellationTokenSource = new CancellationTokenSource(); + var cancellationToken = cancellationTokenSource.Token; + + var operationContext = new OperationContext(stopwatch, timeout, cancellationToken); + + operationContext.Timeout.Should().Be(timeout); + operationContext.RemainingTimeout.Should().Be(timeout); + operationContext.CancellationToken.Should().Be(cancellationToken); + operationContext.ParentContext.Should().BeNull(); + } + + [Fact] + public void RemainingTimeout_should_calculate() + { + var timeout = TimeSpan.FromMilliseconds(500); + var stopwatch = Stopwatch.StartNew(); + Thread.Sleep(10); + stopwatch.Stop(); + + var operationContext = new OperationContext(stopwatch, timeout, CancellationToken.None); + + operationContext.RemainingTimeout.Should().Be(timeout - stopwatch.Elapsed); + } + + [Fact] + public void RemainingTimeout_should_return_infinite_for_infinite_timeout() + { + var stopwatch = Stopwatch.StartNew(); + Thread.Sleep(10); + stopwatch.Stop(); + + var operationContext = new OperationContext(stopwatch, Timeout.InfiniteTimeSpan, CancellationToken.None); + + operationContext.RemainingTimeout.Should().Be(Timeout.InfiniteTimeSpan); + } + + [Fact] + public void RemainingTimeout_could_be_negative() + { + var timeout = TimeSpan.FromMilliseconds(5); + var stopwatch = Stopwatch.StartNew(); + Thread.Sleep(10); + stopwatch.Stop(); + + var operationContext = new OperationContext(stopwatch, timeout, CancellationToken.None); + + operationContext.RemainingTimeout.Should().Be(timeout - stopwatch.Elapsed); + } + + [Theory] + [MemberData(nameof(IsTimedOut_test_cases))] + public void IsTimedOut_should_return_expected_result(bool expected, TimeSpan timeout, TimeSpan waitTime) + { + var stopwatch = Stopwatch.StartNew(); + Thread.Sleep(waitTime); + stopwatch.Stop(); + + var operationContext = new OperationContext(stopwatch, timeout, CancellationToken.None); + var result = operationContext.IsTimedOut(); + + result.Should().Be(expected); + } + + public static IEnumerable IsTimedOut_test_cases = + [ + [false, Timeout.InfiniteTimeSpan, TimeSpan.FromMilliseconds(5)], + [false, TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(5)], + [true, TimeSpan.FromMilliseconds(5), TimeSpan.FromMilliseconds(10)], + ]; + + [Fact] + public void ThrowIfTimedOutOrCanceled_should_not_throw_if_no_timeout_and_no_cancellation() + { + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, CancellationToken.None); + + var exception = Record.Exception(() => operationContext.ThrowIfTimedOutOrCanceled()); + + exception.Should().BeNull(); + } + + [Fact] + public void ThrowIfTimedOutOrCanceled_throws_on_timeout() + { + var operationContext = new OperationContext(TimeSpan.FromMilliseconds(10), CancellationToken.None); + Thread.Sleep(20); + + var exception = Record.Exception(() => operationContext.ThrowIfTimedOutOrCanceled()); + + exception.Should().BeOfType(); + } + + [Fact] + public void ThrowIfTimedOutOrCanceled_throws_on_cancellation() + { + using var cancellationSource = new CancellationTokenSource(); + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationSource.Token); + cancellationSource.Cancel(); + + var exception = Record.Exception(() => operationContext.ThrowIfTimedOutOrCanceled()); + + exception.Should().BeOfType(); + } + + [Fact] + public void ThrowIfTimedOutOrCanceled_throws_CancelledException_when_timedout_and_cancelled() + { + using var cancellationSource = new CancellationTokenSource(); + var operationContext = new OperationContext(TimeSpan.FromMilliseconds(10), cancellationSource.Token); + Thread.Sleep(20); + cancellationSource.Cancel(); + + var exception = Record.Exception(() => operationContext.ThrowIfTimedOutOrCanceled()); + + exception.Should().BeOfType(); + } + + [Theory] + [ParameterAttributeData] + public async Task Wait_should_throw_if_context_is_timedout([Values(true, false)] bool async) + { + var taskCompletionSource = new TaskCompletionSource(); + var operationContext = new OperationContext(TimeSpan.FromMilliseconds(10), CancellationToken.None); + Thread.Sleep(20); + + var exception = async ? + await Record.ExceptionAsync(() => operationContext.WaitTaskAsync(taskCompletionSource.Task)) : + Record.Exception(() => operationContext.WaitTask(taskCompletionSource.Task)); + + exception.Should().BeOfType(); + } + + [Theory] + [ParameterAttributeData] + public async Task Wait_should_throw_if_context_is_cancelled([Values(true, false)] bool async) + { + var taskCompletionSource = new TaskCompletionSource(); + var cancellationTokenSource = new CancellationTokenSource(); + cancellationTokenSource.Cancel(); + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, cancellationTokenSource.Token); + + var exception = async ? + await Record.ExceptionAsync(() => operationContext.WaitTaskAsync(taskCompletionSource.Task)) : + Record.Exception(() => operationContext.WaitTask(taskCompletionSource.Task)); + + exception.Should().BeOfType(); + } + + [Theory] + [ParameterAttributeData] + public async Task Wait_should_rethrow_on_failed_task([Values(true, false)] bool async) + { + var ex = new InvalidOperationException(); + var task = Task.FromException(ex); + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, CancellationToken.None); + + var exception = async ? + await Record.ExceptionAsync(() => operationContext.WaitTaskAsync(task)) : + Record.Exception(() => operationContext.WaitTask(task)); + + exception.Should().Be(ex); + } + + [Theory] + [ParameterAttributeData] + public async Task Wait_should_rethrow_on_failed_promise_task([Values(true, false)] bool async) + { + var ex = new InvalidOperationException("Ups!"); + var taskCompletionSource = new TaskCompletionSource(); + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, CancellationToken.None); + + var task = Task.Run(async () => + { + if (async) + { + await operationContext.WaitTaskAsync(taskCompletionSource.Task); + } + else + { + operationContext.WaitTask(taskCompletionSource.Task); + } + }); + Thread.Sleep(20); + taskCompletionSource.SetException(ex); + + var exception = await Record.ExceptionAsync(() => task); + exception.Should().Be(ex); + } + + [Theory] + [ParameterAttributeData] + public async Task Wait_should_throw_on_timeout([Values(true, false)] bool async) + { + var taskCompletionSource = new TaskCompletionSource(); + var operationContext = new OperationContext(TimeSpan.FromMilliseconds(20), CancellationToken.None); + + var exception = async ? + await Record.ExceptionAsync(() => operationContext.WaitTaskAsync(taskCompletionSource.Task)) : + Record.Exception(() => operationContext.WaitTask(taskCompletionSource.Task)); + + exception.Should().BeOfType(); + } + + [Theory] + [ParameterAttributeData] + public async Task Wait_should_not_throw_on_resolved_task_with_timedout_context([Values(true, false)] bool async) + { + var task = Task.FromResult(42); + var operationContext = new OperationContext(TimeSpan.FromMilliseconds(10), CancellationToken.None); + Thread.Sleep(20); + + var exception = async ? + await Record.ExceptionAsync(() => operationContext.WaitTaskAsync(task)) : + Record.Exception(() => operationContext.WaitTask(task)); + + exception.Should().BeNull(); + } + + + [Theory] + [MemberData(nameof(WithTimeout_test_cases))] + public void WithTimeout_should_calculate_proper_timeout(TimeSpan expected, TimeSpan originalTimeout, TimeSpan newTimeout) + { + var operationContext = new OperationContext(new Stopwatch(), originalTimeout, CancellationToken.None); + var resultContext = operationContext.WithTimeout(newTimeout); + + resultContext.Timeout.Should().Be(expected); + } + + public static IEnumerable WithTimeout_test_cases = + [ + [Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan], + [TimeSpan.FromMilliseconds(5), Timeout.InfiniteTimeSpan, TimeSpan.FromMilliseconds(5)], + [TimeSpan.FromMilliseconds(5), TimeSpan.FromMilliseconds(5), Timeout.InfiniteTimeSpan], + [TimeSpan.FromMilliseconds(5), TimeSpan.FromMilliseconds(5), TimeSpan.FromMilliseconds(10)], + [TimeSpan.FromMilliseconds(5), TimeSpan.FromMilliseconds(10), TimeSpan.FromMilliseconds(5)], + ]; + + [Fact] + public void WithTimeout_should_set_ParentContext() + { + var operationContext = new OperationContext(new Stopwatch(), Timeout.InfiniteTimeSpan, CancellationToken.None); + var resultContext = operationContext.WithTimeout(TimeSpan.FromSeconds(10)); + + resultContext.ParentContext.Should().Be(operationContext); + } + } +} + diff --git a/tests/MongoDB.Driver.Tests/OperationExecutorTests.cs b/tests/MongoDB.Driver.Tests/OperationExecutorTests.cs index ef207d784f6..267d3db0620 100644 --- a/tests/MongoDB.Driver.Tests/OperationExecutorTests.cs +++ b/tests/MongoDB.Driver.Tests/OperationExecutorTests.cs @@ -31,7 +31,7 @@ public class OperationExecutorTests [Fact] public void StartImplicitSession_should_call_cluster_StartSession() { - var subject = CreateSubject(out var clusterMock, out _); + var subject = CreateSubject(out var clusterMock); subject.StartImplicitSession(); @@ -42,8 +42,8 @@ public void StartImplicitSession_should_call_cluster_StartSession() [ParameterAttributeData] public async Task ExecuteReadOperation_throws_on_null_operation([Values(true, false)] bool async) { - var subject = CreateSubject(out _, out _); - var options = new ReadOperationOptions(); + var subject = CreateSubject(out _); + var options = new ReadOperationOptions(Timeout.InfiniteTimeSpan); var session = Mock.Of(); var exception = async ? @@ -58,7 +58,7 @@ await Record.ExceptionAsync(() => subject.ExecuteReadOperationAsync(sess [ParameterAttributeData] public async Task ExecuteReadOperation_throws_on_null_options([Values(true, false)] bool async) { - var subject = CreateSubject(out _, out _); + var subject = CreateSubject(out _); var operation = Mock.Of>(); var session = Mock.Of(); @@ -74,9 +74,9 @@ await Record.ExceptionAsync(() => subject.ExecuteReadOperationAsync(session, ope [ParameterAttributeData] public async Task ExecuteReadOperation_throws_on_null_session([Values(true, false)] bool async) { - var subject = CreateSubject(out _, out _); + var subject = CreateSubject(out _); var operation = Mock.Of>(); - var options = new ReadOperationOptions(); + var options = new ReadOperationOptions(Timeout.InfiniteTimeSpan); var exception = async ? await Record.ExceptionAsync(() => subject.ExecuteReadOperationAsync(null, operation, options, true, CancellationToken.None)) : @@ -90,8 +90,8 @@ await Record.ExceptionAsync(() => subject.ExecuteReadOperationAsync(null, operat [ParameterAttributeData] public async Task ExecuteWriteOperation_throws_on_null_operation([Values(true, false)] bool async) { - var subject = CreateSubject(out _, out _); - var options = new WriteOperationOptions(); + var subject = CreateSubject(out _); + var options = new WriteOperationOptions(Timeout.InfiniteTimeSpan); var session = Mock.Of(); var exception = async ? @@ -106,7 +106,7 @@ await Record.ExceptionAsync(() => subject.ExecuteWriteOperationAsync(ses [ParameterAttributeData] public async Task ExecuteWriteOperation_throws_on_null_options([Values(true, false)] bool async) { - var subject = CreateSubject(out _, out _); + var subject = CreateSubject(out _); var operation = Mock.Of>(); var session = Mock.Of(); @@ -122,9 +122,9 @@ await Record.ExceptionAsync(() => subject.ExecuteWriteOperationAsync(session, op [ParameterAttributeData] public async Task ExecuteWriteOperation_throws_on_null_session([Values(true, false)] bool async) { - var subject = CreateSubject(out _, out _); + var subject = CreateSubject(out _); var operation = Mock.Of>(); - var options = new WriteOperationOptions(); + var options = new WriteOperationOptions(Timeout.InfiniteTimeSpan); var exception = async ? await Record.ExceptionAsync(() => subject.ExecuteWriteOperationAsync(null, operation, options, true, CancellationToken.None)) : @@ -134,23 +134,13 @@ await Record.ExceptionAsync(() => subject.ExecuteWriteOperationAsync(null, opera .Subject.ParamName.Should().Be("session"); } - private OperationExecutor CreateSubject(out Mock clusterMock, out Mock implicitSessionMock) + private OperationExecutor CreateSubject(out Mock clusterMock) { - implicitSessionMock = CreateCoreSessionMock(true); clusterMock = new Mock(); - clusterMock.Setup(c => c.StartSession(It.IsAny())).Returns(implicitSessionMock.Object); var clientMock = new Mock(); clientMock.SetupGet(c => c.Cluster).Returns(clusterMock.Object); return new OperationExecutor(clientMock.Object); } - - private static Mock CreateCoreSessionMock(bool isImplicit) - { - var sessionMock = new Mock(); - sessionMock.SetupGet(s => s.IsImplicit).Returns(isImplicit); - sessionMock.Setup(s => s.Fork()).Returns(() => CreateCoreSessionMock(isImplicit).Object); - return sessionMock; - } } } diff --git a/tests/MongoDB.Driver.Tests/ReadOperationOptionsTests.cs b/tests/MongoDB.Driver.Tests/ReadOperationOptionsTests.cs index 585b2e442ff..eab234fa94b 100644 --- a/tests/MongoDB.Driver.Tests/ReadOperationOptionsTests.cs +++ b/tests/MongoDB.Driver.Tests/ReadOperationOptionsTests.cs @@ -14,6 +14,7 @@ */ using System.Collections.Generic; +using System.Threading; using FluentAssertions; using MongoDB.Driver.Core.Bindings; using Moq; @@ -31,7 +32,7 @@ public void GetEffectiveReadPreferenceTests( ReadPreference defaultReadPreference, IClientSessionHandle session) { - var readOperationOptions = new ReadOperationOptions(explicitReadPreference, defaultReadPreference); + var readOperationOptions = new ReadOperationOptions(Timeout.InfiniteTimeSpan, explicitReadPreference, defaultReadPreference); var result = readOperationOptions.GetEffectiveReadPreference(session); result.Should().Be(expectedReadPreference); diff --git a/tests/MongoDB.Driver.Tests/Specifications/Runner/MongoClientJsonDrivenTestRunnerBase.cs b/tests/MongoDB.Driver.Tests/Specifications/Runner/MongoClientJsonDrivenTestRunnerBase.cs index b0f403147d0..8ad80cafbca 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/Runner/MongoClientJsonDrivenTestRunnerBase.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/Runner/MongoClientJsonDrivenTestRunnerBase.cs @@ -456,11 +456,11 @@ private protected FailPoint ConfigureFailPoint(BsonDocument test, IMongoClient c var serverAddress = EndPointHelper.Parse(settings.Server.ToString()); var selector = new EndPointServerSelector(serverAddress); - _failPointServer = cluster.SelectServer(selector, CancellationToken.None); + _failPointServer = cluster.SelectServer(OperationContext.NoTimeout, selector); } else { - _failPointServer = cluster.SelectServer(WritableServerSelector.Instance, CancellationToken.None); + _failPointServer = cluster.SelectServer(OperationContext.NoTimeout, WritableServerSelector.Instance); } var session = NoCoreSession.NewHandle(); diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs index ceafb5aeae4..73c1076d90d 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs @@ -3245,7 +3245,7 @@ private void DropCollection(CollectionNamespace collectionNamespace, BsonDocumen using (var binding = new WritableServerBinding(_cluster, session.Fork())) using (var bindingHandle = new ReadWriteBindingHandle(binding)) { - operation.Execute(bindingHandle, CancellationToken.None); + operation.Execute(OperationContext.NoTimeout, bindingHandle); } } diff --git a/tests/MongoDB.Driver.Tests/Specifications/connection-monitoring-and-pooling/ConnectionMonitoringAndPoolingTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/connection-monitoring-and-pooling/ConnectionMonitoringAndPoolingTestRunner.cs index 626ef59450a..cad223d7c2d 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/connection-monitoring-and-pooling/ConnectionMonitoringAndPoolingTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/connection-monitoring-and-pooling/ConnectionMonitoringAndPoolingTestRunner.cs @@ -409,18 +409,9 @@ private void ExecuteCheckOut( void CheckOut(BsonDocument op, IConnectionPool cp, ConcurrentDictionary cm) { - IConnection conn; - if (async) - { - conn = cp - .AcquireConnectionAsync(CancellationToken.None) - .GetAwaiter() - .GetResult(); - } - else - { - conn = cp.AcquireConnection(CancellationToken.None); - } + var conn = async ? + cp.AcquireConnectionAsync(OperationContext.NoTimeout).GetAwaiter().GetResult() : + cp.AcquireConnection(OperationContext.NoTimeout); if (op.TryGetValue("label", out var label)) { @@ -680,7 +671,7 @@ private void ParseSettings( connectionIdLocalValueProvider: connectionIdProvider)) .Subscribe(eventCapturer)); - var server = cluster.SelectServer(WritableServerSelector.Instance, CancellationToken.None); + var server = cluster.SelectServer(OperationContext.NoTimeout, WritableServerSelector.Instance); connectionPool = server._connectionPool(); if (test.TryGetValue(Schema.Intergration.failPoint, out var failPointDocument)) @@ -738,7 +729,7 @@ o is ServerHeartbeatSucceededEvent || eventCapturer.WaitForOrThrowIfTimeout(events => events.Any(e => e is ConnectionPoolClearedEvent), TimeSpan.FromMilliseconds(500)); } - var failPointServer = CoreTestConfiguration.Cluster.SelectServer(new EndPointServerSelector(server.EndPoint), default); + var failPointServer = CoreTestConfiguration.Cluster.SelectServer(OperationContext.NoTimeout, new EndPointServerSelector(server.EndPoint)); failPoint = FailPoint.Configure(failPointServer, NoCoreSession.NewHandle(), failPointDocument.AsBsonDocument, withAsync: async); if (resetPool) diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsProseTests.cs index f4aa9cc83ac..e95cf102944 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsProseTests.cs @@ -79,7 +79,7 @@ public async Task PoolClearedError_read_retryablity_test([Values(true, false)] b .Capture() .CaptureCommandEvents("find"); - var failpointServer = DriverTestConfiguration.Client.GetClusterInternal().SelectServer(failPointSelector, default); + var failpointServer = DriverTestConfiguration.Client.GetClusterInternal().SelectServer(OperationContext.NoTimeout, failPointSelector); using var failPoint = FailPoint.Configure(failpointServer, NoCoreSession.NewHandle(), failPointCommand); using var client = CreateClient(settings, eventCapturer, heartbeatInterval); @@ -146,8 +146,8 @@ public void Sharded_cluster_retryable_reads_are_retried_on_different_mongos_if_a }, useMultipleShardRouters: true); - var failPointServer1 = client.GetClusterInternal().SelectServer(new EndPointServerSelector(client.Cluster.Description.Servers[0].EndPoint), default); - var failPointServer2 = client.GetClusterInternal().SelectServer(new EndPointServerSelector(client.Cluster.Description.Servers[1].EndPoint), default); + var failPointServer1 = client.GetClusterInternal().SelectServer(OperationContext.NoTimeout, new EndPointServerSelector(client.Cluster.Description.Servers[0].EndPoint)); + var failPointServer2 = client.GetClusterInternal().SelectServer(OperationContext.NoTimeout, new EndPointServerSelector(client.Cluster.Description.Servers[1].EndPoint)); using var failPoint1 = FailPoint.Configure(failPointServer1, NoCoreSession.NewHandle(), failPointCommand); using var failPoint2 = FailPoint.Configure(failPointServer2, NoCoreSession.NewHandle(), failPointCommand); @@ -196,7 +196,7 @@ public void Sharded_cluster_retryable_reads_are_retried_on_same_mongos_if_no_oth }, useMultipleShardRouters: false); - var failPointServer = client.GetClusterInternal().SelectServer(new EndPointServerSelector(client.Cluster.Description.Servers[0].EndPoint), default); + var failPointServer = client.GetClusterInternal().SelectServer(OperationContext.NoTimeout, new EndPointServerSelector(client.Cluster.Description.Servers[0].EndPoint)); using var failPoint = FailPoint.Configure(failPointServer, NoCoreSession.NewHandle(), failPointCommand); diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/PoolClearRetryability.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/PoolClearRetryability.cs index 9054e532708..78949576f42 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/PoolClearRetryability.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/PoolClearRetryability.cs @@ -28,7 +28,6 @@ using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; -using MongoDB.Driver.TestHelpers; using MongoDB.TestHelpers.XunitExtensions; using Xunit; @@ -83,7 +82,7 @@ public async Task PoolClearedError_write_retryablity_test([Values(false, true)] .Capture() .CaptureCommandEvents("insert"); - var failpointServer = DriverTestConfiguration.Client.GetClusterInternal().SelectServer(failPointSelector, default); + var failpointServer = DriverTestConfiguration.Client.GetClusterInternal().SelectServer(OperationContext.NoTimeout, failPointSelector); using var failPoint = FailPoint.Configure(failpointServer, NoCoreSession.NewHandle(), failPointCommand); using var client = CreateClient(settings, eventCapturer, heartbeatInterval); diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/RetryWriteOnOtherMongos.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/RetryWriteOnOtherMongos.cs index 75f89943f40..c7424f38880 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/RetryWriteOnOtherMongos.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/RetryWriteOnOtherMongos.cs @@ -61,8 +61,8 @@ public void Sharded_cluster_retryable_writes_are_retried_on_different_mongos_if_ }, useMultipleShardRouters: true); - var failPointServer1 = client.GetClusterInternal().SelectServer(new EndPointServerSelector(client.Cluster.Description.Servers[0].EndPoint), default); - var failPointServer2 = client.GetClusterInternal().SelectServer(new EndPointServerSelector(client.Cluster.Description.Servers[1].EndPoint), default); + var failPointServer1 = client.GetClusterInternal().SelectServer(OperationContext.NoTimeout, new EndPointServerSelector(client.Cluster.Description.Servers[0].EndPoint)); + var failPointServer2 = client.GetClusterInternal().SelectServer(OperationContext.NoTimeout, new EndPointServerSelector(client.Cluster.Description.Servers[1].EndPoint)); using var failPoint1 = FailPoint.Configure(failPointServer1, NoCoreSession.NewHandle(), failPointCommand); using var failPoint2 = FailPoint.Configure(failPointServer2, NoCoreSession.NewHandle(), failPointCommand); @@ -112,7 +112,7 @@ public void Sharded_cluster_retryable_writes_are_retried_on_same_mongo_if_no_oth }, useMultipleShardRouters: false); - var failPointServer = client.GetClusterInternal().SelectServer(new EndPointServerSelector(client.Cluster.Description.Servers[0].EndPoint), default); + var failPointServer = client.GetClusterInternal().SelectServer(OperationContext.NoTimeout, new EndPointServerSelector(client.Cluster.Description.Servers[0].EndPoint)); using var failPoint = FailPoint.Configure(failPointServer, NoCoreSession.NewHandle(), failPointCommand); diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs index cebdf47395f..4d61e9c7b60 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs @@ -165,7 +165,7 @@ public void Monitor_sleep_at_least_minHeartbeatFrequencyMS_between_checks() settings.ApplicationName = appName; settings.ServerSelectionTimeout = TimeSpan.FromSeconds(5); - var server = DriverTestConfiguration.Client.GetClusterInternal().SelectServer(new EndPointServerSelector(new DnsEndPoint(serverAddress.Host, serverAddress.Port)), default); + var server = DriverTestConfiguration.Client.GetClusterInternal().SelectServer(OperationContext.NoTimeout, new EndPointServerSelector(new DnsEndPoint(serverAddress.Host, serverAddress.Port))); using var failPoint = FailPoint.Configure(server, NoCoreSession.NewHandle(), failPointCommand); using var client = DriverTestConfiguration.CreateMongoClient(settings); @@ -220,7 +220,7 @@ public void RoundTimeTrip_test() { // Note that the Server Description Equality rule means that ServerDescriptionChangedEvents will not be published. // So we use reflection to obtain the latest RTT instead. - var server = client.GetClusterInternal().SelectServer(WritableServerSelector.Instance, CancellationToken.None); + var server = client.GetClusterInternal().SelectServer(OperationContext.NoTimeout, WritableServerSelector.Instance); var roundTripTimeMonitor = server._monitor()._roundTripTimeMonitor(); var expectedRoundTripTime = TimeSpan.FromMilliseconds(250); var timeout = TimeSpan.FromSeconds(30); // should not be reached without a driver bug @@ -273,7 +273,7 @@ public void ConnectionPool_cleared_on_failed_hello() eventsWaitTimeout); eventCapturer.Clear(); - var failpointServer = DriverTestConfiguration.Client.GetClusterInternal().SelectServer(new EndPointServerSelector(new DnsEndPoint(serverAddress.Host, serverAddress.Port)), default); + var failpointServer = DriverTestConfiguration.Client.GetClusterInternal().SelectServer(OperationContext.NoTimeout, new EndPointServerSelector(new DnsEndPoint(serverAddress.Host, serverAddress.Port))); using var failPoint = FailPoint.Configure(failpointServer, NoCoreSession.NewHandle(), failPointCommand); eventCapturer.WaitForEventOrThrowIfTimeout(eventsWaitTimeout); diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-selection/InWindowTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/server-selection/InWindowTestRunner.cs index 84abad019de..a61e7d6658a 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/server-selection/InWindowTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/server-selection/InWindowTestRunner.cs @@ -82,8 +82,8 @@ public void RunTestDefinition(JsonDrivenTestCase testCase) for (int i = 0; i < testData.iterations; i++) { var selectedServer = testData.async - ? cluster.SelectServerAsync(readPreferenceSelector, default).GetAwaiter().GetResult() - : cluster.SelectServer(readPreferenceSelector, default); + ? cluster.SelectServerAsync(OperationContext.NoTimeout, readPreferenceSelector).GetAwaiter().GetResult() + : cluster.SelectServer(OperationContext.NoTimeout, readPreferenceSelector); selectionHistogram[selectedServer.ServerId]++; } @@ -125,7 +125,6 @@ private MultiServerCluster CreateAndSetupCluster(ClusterDescription clusterDescr serverDescriptionDisconnected = serverDescriptionDisconnected.With(replicaSetConfig: replicaSetConfig); } var serverDescriptionConnected = serverDescriptionDisconnected.With(state: ServerState.Connected); - var operationsCount = operationsCounts.Single(o => endpoint.ToString().EndsWith(o.address)); diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTargetedFailPointOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTargetedFailPointOperation.cs index a1ff0e272c3..97463a719b0 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTargetedFailPointOperation.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTargetedFailPointOperation.cs @@ -14,7 +14,6 @@ */ using System; -using System.Threading; using MongoDB.Bson; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters.ServerSelectors; @@ -53,7 +52,7 @@ public void Execute() _entityMap.RegisterForDispose(client); var cluster = client.GetClusterInternal(); - var server = cluster.SelectServer(new EndPointServerSelector(pinnedServer), CancellationToken.None); + var server = cluster.SelectServer(OperationContext.NoTimeout, new EndPointServerSelector(pinnedServer)); var session = NoCoreSession.NewHandle(); From 5d4827ab8887d01d0ea737b0123e124c0244d016 Mon Sep 17 00:00:00 2001 From: Medha Tiwari <75640645+medhatiwari@users.noreply.github.com> Date: Fri, 13 Jun 2025 01:16:57 +0530 Subject: [PATCH 20/24] CSHARP-5614: Fix deserialization of primitive arrays on Big Endian systems (#1683) --- .../Serializers/PrimitivesArrayReader.cs | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs b/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs index e811edef230..c8b9bd27cd7 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.Runtime.CompilerServices; using MongoDB.Bson.IO; @@ -73,7 +74,8 @@ private static T[] ReadBsonArray( using var buffer = ThreadStaticBuffer.RentBuffer(array.Length); var bytes = buffer.Bytes; - array.GetBytes(0, bytes, 0, array.Length); + array.GetBytes(0, bytes, 0, array.Length); + var span = bytes.AsSpan(); var result = new List(); @@ -82,10 +84,10 @@ private static T[] ReadBsonArray( while (index < maxIndex) { - ValidateBsonType(bsonDataType); + ValidateBsonType(bsonDataType, span); // Skip name - while (bytes[index] != 0) { index++; }; + while (span[index] != 0) { index++; } index++; // Skip string terminating 0 T value = default; @@ -95,21 +97,22 @@ private static T[] ReadBsonArray( { case ConversionType.DoubleToSingle: { - var v = (float)BitConverter.ToDouble(bytes, index); + var v = (float)BinaryPrimitivesCompat.ReadDoubleLittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.DoubleToDouble: { - var v = BitConverter.ToDouble(bytes, index); + var v = BinaryPrimitivesCompat.ReadDoubleLittleEndian(span.Slice(index)); + value = Unsafe.As(ref v); break; } case ConversionType.Decimal128ToDecimal128: { - var lowBits = (ulong)BitConverter.ToInt64(bytes, index); - var highBits = (ulong)BitConverter.ToInt64(bytes, index + 8); + var lowBits = BinaryPrimitives.ReadUInt64LittleEndian(span.Slice(index)); + var highBits = BinaryPrimitives.ReadUInt64LittleEndian(span.Slice(index + 8)); var v = Decimal128.ToDecimal(Decimal128.FromIEEEBits(highBits, lowBits)); value = Unsafe.As(ref v); @@ -117,63 +120,63 @@ private static T[] ReadBsonArray( } case ConversionType.BoolToBool: { - var v = bytes[index] != 0; + var v = span[index] != 0; value = Unsafe.As(ref v); break; } case ConversionType.Int32ToInt8: { - var v = (sbyte)BitConverter.ToInt32(bytes, index); + var v = (sbyte)BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToUInt8: { - var v = (byte)BitConverter.ToInt32(bytes, index); + var v = (byte)BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToInt16: { - var v = (short)BitConverter.ToInt32(bytes, index); + var v = (short)BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToUInt16: { - var v = (ushort)BitConverter.ToInt32(bytes, index); + var v = (ushort)BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToChar: { - var v = BitConverter.ToChar(bytes, index); + var v = (char)(ushort)BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToInt32: { - var v = BitConverter.ToInt32(bytes, index); + var v = BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToUInt32: { - var v = BitConverter.ToUInt32(bytes, index); + var v = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int64ToInt64: { - var v = BitConverter.ToInt64(bytes, index); + var v = BinaryPrimitives.ReadInt64LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int64ToUInt64: { - var v = BitConverter.ToUInt64(bytes, index); + var v = BinaryPrimitives.ReadUInt64LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } @@ -186,13 +189,13 @@ private static T[] ReadBsonArray( index += bsonDataSize; } - ValidateBsonType(BsonType.EndOfDocument); + ValidateBsonType(BsonType.EndOfDocument, span); return result.ToArray(); - void ValidateBsonType(BsonType bsonType) + void ValidateBsonType(BsonType bsonType, Span span) { - if ((BsonType)bytes[index] != bsonType) + if ((BsonType)span[index] != bsonType) { throw new InvalidOperationException(); } From 0cbd39bb3e0f54c0a9c7d391ea845c3c78c0aa38 Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Mon, 16 Jun 2025 08:41:45 -0700 Subject: [PATCH 21/24] CSHARP-5608: CSOT: Command Execution (#1709) --- src/MongoDB.Driver/Core/Clusters/Cluster.cs | 528 +++++++++--------- .../OperationsCountServerSelector.cs | 9 +- .../Core/Jira/CSharp3173Tests.cs | 2 +- .../Core/Jira/CSharp3302Tests.cs | 2 +- .../Core/Logging/EventLoggerTests.cs | 2 +- .../prose-tests/ClientEncryptionProseTests.cs | 2 +- 6 files changed, 270 insertions(+), 275 deletions(-) diff --git a/src/MongoDB.Driver/Core/Clusters/Cluster.cs b/src/MongoDB.Driver/Core/Clusters/Cluster.cs index fbf9cfe7ac5..287a40657aa 100644 --- a/src/MongoDB.Driver/Core/Clusters/Cluster.cs +++ b/src/MongoDB.Driver/Core/Clusters/Cluster.cs @@ -36,7 +36,6 @@ internal abstract class Cluster : IClusterInternal #region static private static readonly TimeSpan __minHeartbeatIntervalDefault = TimeSpan.FromMilliseconds(500); - private static readonly IServerSelector __randomServerSelector = new RandomServerSelector(); public static SemanticVersion MinSupportedServerVersion { get; } = WireVersion.ToServerVersion(WireVersion.SupportedWireVersionRange.Min); public static Range SupportedWireVersionRange { get; } = WireVersion.SupportedWireVersionRange; @@ -46,18 +45,15 @@ internal abstract class Cluster : IClusterInternal private readonly TimeSpan _minHeartbeatInterval = __minHeartbeatIntervalDefault; private readonly IClusterClock _clusterClock = new ClusterClock(); private readonly ClusterId _clusterId; - private ClusterDescriptionChangeSource _descriptionWithChangedTaskCompletionSource; + private ExpirableClusterDescription _expirableClusterDescription; private readonly LatencyLimitingServerSelector _latencyLimitingServerSelector; protected readonly EventLogger _clusterEventLogger; protected readonly EventLogger _serverSelectionEventLogger; - private Timer _rapidHeartbeatTimer; - private readonly object _serverSelectionWaitQueueLock = new object(); - private int _serverSelectionWaitQueueSize; private readonly IClusterableServerFactory _serverFactory; + private readonly ServerSelectionWaitQueue _serverSelectionWaitQueue; private readonly ICoreServerSessionPool _serverSessionPool; private readonly ClusterSettings _settings; private readonly InterlockedInt32 _state; - private readonly InterlockedInt32 _rapidHeartbeatTimerCallbackState; // constructors protected Cluster(ClusterSettings settings, IClusterableServerFactory serverFactory, IEventSubscriber eventSubscriber, ILoggerFactory loggerFactory) @@ -67,15 +63,11 @@ protected Cluster(ClusterSettings settings, IClusterableServerFactory serverFact _serverFactory = Ensure.IsNotNull(serverFactory, nameof(serverFactory)); Ensure.IsNotNull(eventSubscriber, nameof(eventSubscriber)); _state = new InterlockedInt32(State.Initial); - _rapidHeartbeatTimerCallbackState = new InterlockedInt32(RapidHeartbeatTimerCallbackState.NotRunning); _clusterId = new ClusterId(); - _descriptionWithChangedTaskCompletionSource = new (ClusterDescription.CreateInitial(_clusterId, _settings.DirectConnection)); + _expirableClusterDescription = new (this, ClusterDescription.CreateInitial(_clusterId, _settings.DirectConnection)); _latencyLimitingServerSelector = new LatencyLimitingServerSelector(settings.LocalThreshold); - - _rapidHeartbeatTimer = new Timer(RapidHeartbeatTimerCallback, null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); - + _serverSelectionWaitQueue = new ServerSelectionWaitQueue(this); _serverSessionPool = new CoreServerSessionPool(this); - _clusterEventLogger = loggerFactory.CreateEventLogger(eventSubscriber); _serverSelectionEventLogger = loggerFactory.CreateEventLogger(eventSubscriber); } @@ -93,7 +85,7 @@ public ClusterDescription Description { get { - return _descriptionWithChangedTaskCompletionSource.ClusterDescription; + return _expirableClusterDescription.ClusterDescription; } } @@ -127,53 +119,19 @@ protected virtual void Dispose(bool disposing) var newClusterDescription = new ClusterDescription( _clusterId, - _descriptionWithChangedTaskCompletionSource.ClusterDescription.DirectConnection, + _expirableClusterDescription.ClusterDescription.DirectConnection, dnsMonitorException: null, ClusterType.Unknown, Enumerable.Empty()); UpdateClusterDescription(newClusterDescription); - _rapidHeartbeatTimer.Dispose(); + _serverSelectionWaitQueue.Dispose(); _clusterEventLogger.Logger?.LogTrace(_clusterId, "Cluster disposed"); } } - private void EnterServerSelectionWaitQueue(IServerSelector selector, ClusterDescription clusterDescription, long? operationId, TimeSpan remainingTime) - { - lock (_serverSelectionWaitQueueLock) - { - if (_serverSelectionWaitQueueSize >= _settings.MaxServerSelectionWaitQueueSize) - { - throw MongoWaitQueueFullException.ForServerSelection(); - } - - if (++_serverSelectionWaitQueueSize == 1) - { - _rapidHeartbeatTimer.Change(TimeSpan.Zero, _minHeartbeatInterval); - } - - _serverSelectionEventLogger.LogAndPublish(new ClusterEnteredSelectionQueueEvent( - clusterDescription, - selector, - operationId, - EventContext.OperationName, - remainingTime)); - } - } - - private void ExitServerSelectionWaitQueue() - { - lock (_serverSelectionWaitQueueLock) - { - if (--_serverSelectionWaitQueueSize == 0) - { - _rapidHeartbeatTimer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); - } - } - } - public virtual void Initialize() { ThrowIfDisposed(); @@ -183,28 +141,6 @@ public virtual void Initialize() } } - private void RapidHeartbeatTimerCallback(object args) - { - // avoid requesting heartbeat reentrantly - if (_rapidHeartbeatTimerCallbackState.TryChange(RapidHeartbeatTimerCallbackState.NotRunning, RapidHeartbeatTimerCallbackState.Running)) - { - try - { - RequestHeartbeat(); - } - catch - { - // TODO: Trace this - // If we don't protect this call, we could - // take down the app domain. - } - finally - { - _rapidHeartbeatTimerCallbackState.TryChange(RapidHeartbeatTimerCallbackState.NotRunning); - } - } - } - protected abstract void RequestHeartbeat(); protected void OnDescriptionChanged(ClusterDescription oldDescription, ClusterDescription newDescription, bool shouldClusterDescriptionChangedEventBePublished) @@ -219,80 +155,78 @@ protected void OnDescriptionChanged(ClusterDescription oldDescription, ClusterDe public IServer SelectServer(OperationContext operationContext, IServerSelector selector) { - ThrowIfDisposedOrNotOpen(); Ensure.IsNotNull(selector, nameof(selector)); Ensure.IsNotNull(operationContext, nameof(operationContext)); + ThrowIfDisposedOrNotOpen(); - var serverSelectionOperationContext = operationContext.WithTimeout(Settings.ServerSelectionTimeout); + operationContext = operationContext.WithTimeout(Settings.ServerSelectionTimeout); + var expirableClusterDescription = _expirableClusterDescription; + IDisposable serverSelectionWaitQueueDisposer = null; + (selector, var operationCountSelector, var stopwatch) = BeginServerSelection(expirableClusterDescription.ClusterDescription, selector); - using (var helper = new SelectServerHelper(this, selector)) + try { - try + while (true) { - while (true) + var result = SelectServer(expirableClusterDescription, selector, operationCountSelector); + if (result != default) { - var server = helper.SelectServer(serverSelectionOperationContext); - if (server != null) - { - return server; - } - - helper.WaitForDescriptionChanged(serverSelectionOperationContext); + EndServerSelection(expirableClusterDescription.ClusterDescription, selector, result.ServerDescription, stopwatch); + return result.Server; } - } - catch (TimeoutException) - { - var message = BuildTimeoutExceptionMessage(_settings.ServerSelectionTimeout, selector, helper.Description); - var timeoutException = new TimeoutException(message); - helper.HandleException(timeoutException); - throw timeoutException; - } - catch (Exception ex) - { - helper.HandleException(ex); - throw; + serverSelectionWaitQueueDisposer ??= _serverSelectionWaitQueue.Enter(operationContext, selector, expirableClusterDescription.ClusterDescription, EventContext.OperationId); + + operationContext.WaitTask(expirableClusterDescription.Expired); + expirableClusterDescription = _expirableClusterDescription; } } + catch (Exception ex) + { + throw HandleServerSelectionException(expirableClusterDescription.ClusterDescription, selector, ex, stopwatch); + } + finally + { + serverSelectionWaitQueueDisposer?.Dispose(); + } } public async Task SelectServerAsync(OperationContext operationContext, IServerSelector selector) { - ThrowIfDisposedOrNotOpen(); Ensure.IsNotNull(selector, nameof(selector)); Ensure.IsNotNull(operationContext, nameof(operationContext)); + ThrowIfDisposedOrNotOpen(); - var serverSelectionOperationContext = operationContext.WithTimeout(Settings.ServerSelectionTimeout); + operationContext = operationContext.WithTimeout(Settings.ServerSelectionTimeout); + var expirableClusterDescription = _expirableClusterDescription; + IDisposable serverSelectionWaitQueueDisposer = null; + (selector, var operationCountSelector, var stopwatch) = BeginServerSelection(expirableClusterDescription.ClusterDescription, selector); - using (var helper = new SelectServerHelper(this, selector)) + try { - try + while (true) { - while (true) + var result = SelectServer(expirableClusterDescription, selector, operationCountSelector); + if (result != default) { - var server = helper.SelectServer(serverSelectionOperationContext); - if (server != null) - { - return server; - } - - await helper.WaitForDescriptionChangedAsync(serverSelectionOperationContext).ConfigureAwait(false); + EndServerSelection(expirableClusterDescription.ClusterDescription, selector, result.ServerDescription, stopwatch); + return result.Server; } - } - catch (TimeoutException) - { - var message = BuildTimeoutExceptionMessage(_settings.ServerSelectionTimeout, selector, helper.Description); - var timeoutException = new TimeoutException(message); - helper.HandleException(timeoutException); - throw timeoutException; - } - catch (Exception ex) - { - helper.HandleException(ex); - throw; + serverSelectionWaitQueueDisposer ??= _serverSelectionWaitQueue.Enter(operationContext, selector, expirableClusterDescription.ClusterDescription, EventContext.OperationId); + + await operationContext.WaitTaskAsync(expirableClusterDescription.Expired).ConfigureAwait(false); + expirableClusterDescription = _expirableClusterDescription; } } + catch (Exception ex) + { + throw HandleServerSelectionException(expirableClusterDescription.ClusterDescription, selector, ex, stopwatch); + } + finally + { + serverSelectionWaitQueueDisposer?.Dispose(); + } } public ICoreSessionHandle StartSession(CoreSessionOptions options) @@ -306,21 +240,91 @@ public ICoreSessionHandle StartSession(CoreSessionOptions options) protected void UpdateClusterDescription(ClusterDescription newClusterDescription, bool shouldClusterDescriptionChangedEventBePublished = true) { - var oldClusterDescription = Interlocked.Exchange(ref _descriptionWithChangedTaskCompletionSource, new(newClusterDescription)); + var expiredClusterDescription = Interlocked.Exchange(ref _expirableClusterDescription, new(this, newClusterDescription)); - OnDescriptionChanged(oldClusterDescription.ClusterDescription, newClusterDescription, shouldClusterDescriptionChangedEventBePublished); + OnDescriptionChanged(expiredClusterDescription.ClusterDescription, newClusterDescription, shouldClusterDescriptionChangedEventBePublished); - oldClusterDescription.TrySetChanged(); + expiredClusterDescription.TrySetExpired(); } - private string BuildTimeoutExceptionMessage(TimeSpan timeout, IServerSelector selector, ClusterDescription clusterDescription) + private (IServerSelector Selector, OperationsCountServerSelector OperationCountSelector, Stopwatch Stopwatch) BeginServerSelection(ClusterDescription clusterDescription, IServerSelector selector) { - var ms = (int)Math.Round(timeout.TotalMilliseconds); - return string.Format( - "A timeout occurred after {0}ms selecting a server using {1}. Client view of cluster state is {2}.", - ms.ToString(), - selector.ToString(), - clusterDescription.ToString()); + _serverSelectionEventLogger.LogAndPublish(new ClusterSelectingServerEvent( + clusterDescription, + selector, + EventContext.OperationId, + EventContext.OperationName)); + + var allSelectors = new List(5); + if (Settings.PreServerSelector != null) + { + allSelectors.Add(Settings.PreServerSelector); + } + + allSelectors.Add(selector); + if (Settings.PostServerSelector != null) + { + allSelectors.Add(Settings.PostServerSelector); + } + + allSelectors.Add(_latencyLimitingServerSelector); + var operationCountSelector = new OperationsCountServerSelector(Array.Empty()); + allSelectors.Add(operationCountSelector); + + return (new CompositeServerSelector(allSelectors), operationCountSelector, Stopwatch.StartNew()); + } + + private void EndServerSelection(ClusterDescription clusterDescription, IServerSelector selector, ServerDescription selectedServerDescription, Stopwatch stopwatch) + { + stopwatch.Stop(); + _serverSelectionEventLogger.LogAndPublish(new ClusterSelectedServerEvent( + clusterDescription, + selector, + selectedServerDescription, + stopwatch.Elapsed, + EventContext.OperationId, + EventContext.OperationName)); + } + + private Exception HandleServerSelectionException(ClusterDescription clusterDescription, IServerSelector selector, Exception exception, Stopwatch stopwatch) + { + stopwatch.Stop(); + + if (exception is TimeoutException) + { + var message = $"A timeout occurred after {stopwatch.ElapsedMilliseconds}ms selecting a server using {selector}. Client view of cluster state is {clusterDescription}."; + exception = new TimeoutException(message); + } + + _serverSelectionEventLogger.LogAndPublish(new ClusterSelectingServerFailedEvent( + clusterDescription, + selector, + exception, + EventContext.OperationId, + EventContext.OperationName)); + + return exception; + } + + private (IClusterableServer Server, ServerDescription ServerDescription) SelectServer(ExpirableClusterDescription clusterDescriptionChangeSource, IServerSelector selector, OperationsCountServerSelector operationCountSelector) + { + MongoIncompatibleDriverException.ThrowIfNotSupported(clusterDescriptionChangeSource.ClusterDescription); + + operationCountSelector.PopulateServers(clusterDescriptionChangeSource.ConnectedServers); + var selectedServerDescription = selector + .SelectServers(clusterDescriptionChangeSource.ClusterDescription, clusterDescriptionChangeSource.ConnectedServerDescriptions) + .SingleOrDefault(); + + if (selectedServerDescription != null) + { + var selectedServer = clusterDescriptionChangeSource.ConnectedServers.FirstOrDefault(s => EndPointHelper.Equals(s.EndPoint, selectedServerDescription.EndPoint)); + if (selectedServer != null) + { + return (selectedServer, selectedServerDescription); + } + } + + return default; } private void ThrowIfDisposed() @@ -341,201 +345,187 @@ private void ThrowIfDisposedOrNotOpen() } // nested classes - internal sealed class ClusterDescriptionChangeSource + internal sealed class ExpirableClusterDescription { - private readonly TaskCompletionSource _changedTaskCompletionSource; + private readonly Cluster _cluster; + private readonly TaskCompletionSource _expireCompletionSource; private readonly ClusterDescription _clusterDescription; + private readonly object _connectedServersLock = new(); + private IReadOnlyList _connectedServers; + private IReadOnlyList _connectedServerDescriptions; - public ClusterDescriptionChangeSource(ClusterDescription clusterDescription) + public ExpirableClusterDescription(Cluster cluster, ClusterDescription clusterDescription) { - _changedTaskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + _cluster = cluster; _clusterDescription = clusterDescription; + _expireCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); } public ClusterDescription ClusterDescription => _clusterDescription; - public Task Changed => _changedTaskCompletionSource.Task; - - public bool TrySetChanged() - => _changedTaskCompletionSource.TrySetResult(true); - } - - private class SelectServerHelper : IDisposable - { - private readonly Cluster _cluster; - private readonly List _connectedServers; - private readonly List _connectedServerDescriptions; - private ClusterDescription _description; - private Task _descriptionChangedTask; - private bool _serverSelectionWaitQueueEntered; - private readonly IServerSelector _selector; - private readonly OperationsCountServerSelector _operationCountServerSelector; - private readonly Stopwatch _stopwatch; - - public SelectServerHelper(Cluster cluster, IServerSelector selector) - { - _cluster = cluster; - _connectedServers = new List(_cluster._descriptionWithChangedTaskCompletionSource.ClusterDescription?.Servers?.Count ?? 1); - _connectedServerDescriptions = new List(_connectedServers.Count); - _operationCountServerSelector = new OperationsCountServerSelector(_connectedServers); - - _stopwatch = Stopwatch.StartNew(); - _selector = DecorateSelector(selector); - } + public Task Expired => _expireCompletionSource.Task; - public ClusterDescription Description + public IReadOnlyList ConnectedServers { - get { return _description; } - } - - public Task DescriptionChangedTask - { - get { return _descriptionChangedTask; } + get + { + EnsureConnectedServersInitialized(); + return _connectedServers; + } } - public IServerSelector Selector + public IReadOnlyList ConnectedServerDescriptions { - get { return _selector; } - } - - public void Dispose() - { - if (_serverSelectionWaitQueueEntered) + get { - _cluster.ExitServerSelectionWaitQueue(); + EnsureConnectedServersInitialized(); + return _connectedServerDescriptions; } } - public void HandleException(Exception exception) - { - _cluster._serverSelectionEventLogger.LogAndPublish(new ClusterSelectingServerFailedEvent( - _description, - _selector, - exception, - EventContext.OperationId, - EventContext.OperationName)); - } + public bool TrySetExpired() + => _expireCompletionSource.TrySetResult(true); - public IServer SelectServer(OperationContext operationContext) + private void EnsureConnectedServersInitialized() { - var clusterDescription = _cluster._descriptionWithChangedTaskCompletionSource; - _descriptionChangedTask = clusterDescription.Changed; - _description = clusterDescription.ClusterDescription; - - if (!_serverSelectionWaitQueueEntered) + if (_connectedServers != null) { - // this is our first time through... - _cluster._serverSelectionEventLogger.LogAndPublish(new ClusterSelectingServerEvent( - _description, - _selector, - EventContext.OperationId, - EventContext.OperationName)); + return; } - MongoIncompatibleDriverException.ThrowIfNotSupported(_description); - - _connectedServers.Clear(); - _connectedServerDescriptions.Clear(); - - foreach (var description in _description.Servers) + lock (_connectedServersLock) { - if (description.State == ServerState.Connected && - _cluster.TryGetServer(description.EndPoint, out var server)) + if (_connectedServers != null) { - _connectedServers.Add(server); - _connectedServerDescriptions.Add(description); + return; } - } - var selectedServersDescriptions = _selector - .SelectServers(_description, _connectedServerDescriptions) - .ToList(); + var connectedServerDescriptions = new List(ClusterDescription.Servers?.Count ?? 1); + var connectedServers = new List(connectedServerDescriptions.Capacity); - IServer selectedServer = null; - - if (selectedServersDescriptions.Count > 0) - { - var selectedServerDescription = selectedServersDescriptions.Count == 1 - ? selectedServersDescriptions[0] - : __randomServerSelector.SelectServers(_description, selectedServersDescriptions).Single(); + if (ClusterDescription.Servers != null) + { + foreach (var description in ClusterDescription.Servers) + { + if (description.State == ServerState.Connected && + _cluster.TryGetServer(description.EndPoint, out var server)) + { + connectedServers.Add(server); + connectedServerDescriptions.Add(description); + } + } + } - selectedServer = _connectedServers.FirstOrDefault(s => EndPointHelper.Equals(s.EndPoint, selectedServerDescription.EndPoint)); + _connectedServerDescriptions = connectedServerDescriptions; + _connectedServers = connectedServers; } + } + } - if (selectedServer != null) - { - _stopwatch.Stop(); - - _cluster._serverSelectionEventLogger.LogAndPublish(new ClusterSelectedServerEvent( - _description, - _selector, - selectedServer.Description, - _stopwatch.Elapsed, - EventContext.OperationId, - EventContext.OperationName)); - } + private static class State + { + public const int Initial = 0; + public const int Open = 1; + public const int Disposed = 2; + } - return selectedServer; - } + private static class RapidHeartbeatTimerCallbackState + { + public const int NotRunning = 0; + public const int Running = 1; + } + + private sealed class ServerSelectionWaitQueue : IDisposable + { + private readonly Cluster _cluster; + private readonly object _serverSelectionWaitQueueLock = new(); + private readonly Timer _rapidHeartbeatTimer; + private readonly InterlockedInt32 _rapidHeartbeatTimerCallbackState; - public void WaitForDescriptionChanged(OperationContext operationContext) + private int _serverSelectionWaitQueueSize; + + public ServerSelectionWaitQueue(Cluster cluster) { - EnsureEnteredServerSelectionQueue(operationContext); - operationContext.WaitTask(DescriptionChangedTask); + _cluster = cluster; + _rapidHeartbeatTimerCallbackState = new InterlockedInt32(RapidHeartbeatTimerCallbackState.NotRunning); + _rapidHeartbeatTimer = new Timer(RapidHeartbeatTimerCallback, null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); } - public Task WaitForDescriptionChangedAsync(OperationContext operationContext) + public void Dispose() { - EnsureEnteredServerSelectionQueue(operationContext); - return operationContext.WaitTaskAsync(DescriptionChangedTask); + _rapidHeartbeatTimer.Dispose(); } - private void EnsureEnteredServerSelectionQueue(OperationContext operationContext) + public IDisposable Enter(OperationContext operationContext, IServerSelector selector, ClusterDescription clusterDescription, long? operationId) { - if (_serverSelectionWaitQueueEntered) + lock (_serverSelectionWaitQueueLock) { - return; + if (_serverSelectionWaitQueueSize >= _cluster._settings.MaxServerSelectionWaitQueueSize) + { + throw MongoWaitQueueFullException.ForServerSelection(); + } + + if (++_serverSelectionWaitQueueSize == 1) + { + _rapidHeartbeatTimer.Change(TimeSpan.Zero, _cluster._minHeartbeatInterval); + } + + _cluster._serverSelectionEventLogger.LogAndPublish(new ClusterEnteredSelectionQueueEvent( + clusterDescription, + selector, + operationId, + EventContext.OperationName, + operationContext.RemainingTimeout)); } - _cluster.EnterServerSelectionWaitQueue(_selector, _description, EventContext.OperationId, operationContext.RemainingTimeout); - _serverSelectionWaitQueueEntered = true; + return new ServerSelectionQueueDisposer(this); } - private IServerSelector DecorateSelector(IServerSelector selector) + private void ExitServerSelectionWaitQueue() { - var settings = _cluster.Settings; - var allSelectors = new List(); + lock (_serverSelectionWaitQueueLock) + { + if (--_serverSelectionWaitQueueSize == 0) + { + _rapidHeartbeatTimer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); + } + } + } - if (settings.PreServerSelector != null) + private void RapidHeartbeatTimerCallback(object args) + { + // avoid requesting heartbeat reentrantly + if (_rapidHeartbeatTimerCallbackState.TryChange(RapidHeartbeatTimerCallbackState.NotRunning, RapidHeartbeatTimerCallbackState.Running)) { - allSelectors.Add(settings.PreServerSelector); + try + { + _cluster.RequestHeartbeat(); + } + catch + { + // TODO: Trace this + // If we don't protect this call, we could + // take down the app domain. + } + finally + { + _rapidHeartbeatTimerCallbackState.TryChange(RapidHeartbeatTimerCallbackState.NotRunning); + } } + } - allSelectors.Add(selector); + private sealed class ServerSelectionQueueDisposer : IDisposable + { + private readonly ServerSelectionWaitQueue _waitQueue; - if (settings.PostServerSelector != null) + public ServerSelectionQueueDisposer(ServerSelectionWaitQueue waitQueue) { - allSelectors.Add(settings.PostServerSelector); + _waitQueue = waitQueue; } - allSelectors.Add(_cluster._latencyLimitingServerSelector); - allSelectors.Add(_operationCountServerSelector); - - return new CompositeServerSelector(allSelectors); + public void Dispose() + => _waitQueue.ExitServerSelectionWaitQueue(); } } - - private static class State - { - public const int Initial = 0; - public const int Open = 1; - public const int Disposed = 2; - } - - private static class RapidHeartbeatTimerCallbackState - { - public const int NotRunning = 0; - public const int Running = 1; - } } } diff --git a/src/MongoDB.Driver/Core/Clusters/ServerSelectors/OperationsCountServerSelector.cs b/src/MongoDB.Driver/Core/Clusters/ServerSelectors/OperationsCountServerSelector.cs index 3442d6bd436..c6cf48bffcb 100644 --- a/src/MongoDB.Driver/Core/Clusters/ServerSelectors/OperationsCountServerSelector.cs +++ b/src/MongoDB.Driver/Core/Clusters/ServerSelectors/OperationsCountServerSelector.cs @@ -22,9 +22,9 @@ namespace MongoDB.Driver.Core.Clusters.ServerSelectors { internal sealed class OperationsCountServerSelector : IServerSelector { - private readonly IEnumerable _clusterableServers; + private IReadOnlyList _clusterableServers; - public OperationsCountServerSelector(IEnumerable clusterableServers) + public OperationsCountServerSelector(IReadOnlyList clusterableServers) { _clusterableServers = clusterableServers; } @@ -58,6 +58,11 @@ public IEnumerable SelectServers(ClusterDescription cluster, } } + public void PopulateServers(IReadOnlyList clusterableServers) + { + _clusterableServers = clusterableServers; + } + /// public override string ToString() => nameof(OperationsCountServerSelector); diff --git a/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3173Tests.cs b/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3173Tests.cs index 70ff0840d24..8be118b9db5 100644 --- a/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3173Tests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3173Tests.cs @@ -289,7 +289,7 @@ private IServerSelector CreateWritableServerAndEndPointSelector(EndPoint endPoin private void ForceClusterId(MultiServerCluster cluster, ClusterId clusterId) { Reflector.SetFieldValue(cluster, "_clusterId", clusterId); - Reflector.SetFieldValue(cluster, "_descriptionWithChangedTaskCompletionSource", new Cluster.ClusterDescriptionChangeSource(ClusterDescription.CreateInitial(clusterId, __directConnection))); + Reflector.SetFieldValue(cluster, "_expirableClusterDescription", new Cluster.ExpirableClusterDescription(cluster, ClusterDescription.CreateInitial(clusterId, __directConnection))); } private void SetupServerMonitorConnection( diff --git a/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3302Tests.cs b/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3302Tests.cs index a3c5488e56f..aa944ed0ca6 100644 --- a/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3302Tests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Jira/CSharp3302Tests.cs @@ -270,7 +270,7 @@ private IServerSelector CreateWritableServerAndEndPointSelector(EndPoint endPoin private void ForceClusterId(MultiServerCluster cluster, ClusterId clusterId) { Reflector.SetFieldValue(cluster, "_clusterId", clusterId); - Reflector.SetFieldValue(cluster, "_descriptionWithChangedTaskCompletionSource", new Cluster.ClusterDescriptionChangeSource(ClusterDescription.CreateInitial(clusterId, __directConnection))); + Reflector.SetFieldValue(cluster, "_expirableClusterDescription", new Cluster.ExpirableClusterDescription(cluster, ClusterDescription.CreateInitial(clusterId, __directConnection))); } private void SetupServerMonitorConnection( diff --git a/tests/MongoDB.Driver.Tests/Core/Logging/EventLoggerTests.cs b/tests/MongoDB.Driver.Tests/Core/Logging/EventLoggerTests.cs index 2d8e97168dd..8d07de219b4 100644 --- a/tests/MongoDB.Driver.Tests/Core/Logging/EventLoggerTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Logging/EventLoggerTests.cs @@ -94,7 +94,7 @@ private static IEnumerable EventsData() (new LogCategories.Command(), new CommandStartedEvent("test", new Bson.BsonDocument(), new DatabaseNamespace("test"), 1, 1, connectionId)), (new LogCategories.Connection(), new ConnectionCreatedEvent(connectionId, null, 1)), (new LogCategories.SDAM(), new ServerHeartbeatStartedEvent(connectionId, true)), - (new LogCategories.ServerSelection(), new ClusterSelectingServerEvent(clusterDescription, new RandomServerSelector(), default, default)) + (new LogCategories.ServerSelection(), new ClusterSelectingServerEvent(clusterDescription, Mock.Of(), default, default)) }; var booleanValues = new[] { true, false }; diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs index 73c1076d90d..bfb4da43d5f 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs @@ -447,7 +447,7 @@ public void BypassSpawningMongocryptdTest( var exception = Record.Exception(() => adminDatabase.RunCommand(legacyHelloCommand)); exception.Should().BeOfType(); - exception.Message.Should().Contain("A timeout occurred after 1000ms selecting a server").And.Contain("localhost:27021"); + exception.Message.Should().MatchRegex(@".*A timeout occurred after \d+ms selecting a server.*").And.Contain("localhost:27021"); } IMongoClient EnsureEnvironmentAndConfigureTestClientEncrypted() From 3f60b851b50a8bae73e1d0481f71cd4ec9364d8e Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Wed, 18 Jun 2025 08:59:44 -0700 Subject: [PATCH 22/24] CSHARP-5617: Remove unused IChannel.Query methods (#1711) --- src/MongoDB.Driver/Core/Bindings/IChannel.cs | 68 ------- src/MongoDB.Driver/Core/Servers/Server.cs | 188 ------------------ .../Core/WireProtocol/QueryWireProtocol.cs | 182 ----------------- 3 files changed, 438 deletions(-) delete mode 100644 src/MongoDB.Driver/Core/WireProtocol/QueryWireProtocol.cs diff --git a/src/MongoDB.Driver/Core/Bindings/IChannel.cs b/src/MongoDB.Driver/Core/Bindings/IChannel.cs index f58ba48103e..275d6cdebbc 100644 --- a/src/MongoDB.Driver/Core/Bindings/IChannel.cs +++ b/src/MongoDB.Driver/Core/Bindings/IChannel.cs @@ -59,74 +59,6 @@ Task CommandAsync( IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken); - - CursorBatch Query( - CollectionNamespace collectionNamespace, - BsonDocument query, - BsonDocument fields, - IElementNameValidator queryValidator, - int skip, - int batchSize, - bool secondaryOk, - bool partialOk, - bool noCursorTimeout, - bool tailableCursor, - bool awaitData, - IBsonSerializer serializer, - MessageEncoderSettings messageEncoderSettings, - CancellationToken cancellationToken); - - [Obsolete("Use an overload that does not have an oplogReplay parameter instead.")] - CursorBatch Query( - CollectionNamespace collectionNamespace, - BsonDocument query, - BsonDocument fields, - IElementNameValidator queryValidator, - int skip, - int batchSize, - bool secondaryOk, - bool partialOk, - bool noCursorTimeout, - bool oplogReplay, // obsolete: OplogReplay is ignored by server versions 4.4.0 and newer - bool tailableCursor, - bool awaitData, - IBsonSerializer serializer, - MessageEncoderSettings messageEncoderSettings, - CancellationToken cancellationToken); - - Task> QueryAsync( - CollectionNamespace collectionNamespace, - BsonDocument query, - BsonDocument fields, - IElementNameValidator queryValidator, - int skip, - int batchSize, - bool secondaryOk, - bool partialOk, - bool noCursorTimeout, - bool tailableCursor, - bool awaitData, - IBsonSerializer serializer, - MessageEncoderSettings messageEncoderSettings, - CancellationToken cancellationToken); - - [Obsolete("Use an overload that does not have an oplogReplay parameter instead.")] - Task> QueryAsync( - CollectionNamespace collectionNamespace, - BsonDocument query, - BsonDocument fields, - IElementNameValidator queryValidator, - int skip, - int batchSize, - bool secondaryOk, - bool partialOk, - bool noCursorTimeout, - bool oplogReplay, // obsolete: OplogReplay is ignored by server versions 4.4.0 and newer - bool tailableCursor, - bool awaitData, - IBsonSerializer serializer, - MessageEncoderSettings messageEncoderSettings, - CancellationToken cancellationToken); } internal interface IChannelHandle : IChannel diff --git a/src/MongoDB.Driver/Core/Servers/Server.cs b/src/MongoDB.Driver/Core/Servers/Server.cs index fa93175f1c0..c7fe1a94b28 100644 --- a/src/MongoDB.Driver/Core/Servers/Server.cs +++ b/src/MongoDB.Driver/Core/Servers/Server.cs @@ -379,176 +379,11 @@ public void Dispose() } } - public CursorBatch Query( - CollectionNamespace collectionNamespace, - BsonDocument query, - BsonDocument fields, - IElementNameValidator queryValidator, - int skip, - int batchSize, - bool secondaryOk, - bool partialOk, - bool noCursorTimeout, - bool tailableCursor, - bool awaitData, - IBsonSerializer serializer, - MessageEncoderSettings messageEncoderSettings, - CancellationToken cancellationToken) - { -#pragma warning disable 618 - return Query( - collectionNamespace, - query, - fields, - queryValidator, - skip, - batchSize, - secondaryOk, - partialOk, - noCursorTimeout, - oplogReplay: false, - tailableCursor, - awaitData, - serializer, - messageEncoderSettings, - cancellationToken); -#pragma warning restore 618 - } - - [Obsolete("Use the newest overload instead.")] - public CursorBatch Query( - CollectionNamespace collectionNamespace, - BsonDocument query, - BsonDocument fields, - IElementNameValidator queryValidator, - int skip, - int batchSize, - bool secondaryOk, - bool partialOk, - bool noCursorTimeout, - bool oplogReplay, - bool tailableCursor, - bool awaitData, - IBsonSerializer serializer, - MessageEncoderSettings messageEncoderSettings, - CancellationToken cancellationToken) - { - secondaryOk = GetEffectiveSecondaryOk(secondaryOk); -#pragma warning disable 618 - var protocol = new QueryWireProtocol( - collectionNamespace, - query, - fields, - queryValidator, - skip, - batchSize, - secondaryOk, - partialOk, - noCursorTimeout, - oplogReplay, - tailableCursor, - awaitData, - serializer, - messageEncoderSettings); -#pragma warning restore 618 - - return ExecuteProtocol(protocol, cancellationToken); - } - - public Task> QueryAsync( - CollectionNamespace collectionNamespace, - BsonDocument query, - BsonDocument fields, - IElementNameValidator queryValidator, - int skip, - int batchSize, - bool secondaryOk, - bool partialOk, - bool noCursorTimeout, - bool tailableCursor, - bool awaitData, - IBsonSerializer serializer, - MessageEncoderSettings messageEncoderSettings, - CancellationToken cancellationToken) - { -#pragma warning disable 618 - return QueryAsync( - collectionNamespace, - query, - fields, - queryValidator, - skip, - batchSize, - secondaryOk, - partialOk, - noCursorTimeout, - oplogReplay: false, - tailableCursor, - awaitData, - serializer, - messageEncoderSettings, - cancellationToken); -#pragma warning restore 618 - } - - [Obsolete("Use the newest overload instead.")] - public Task> QueryAsync( - CollectionNamespace collectionNamespace, - BsonDocument query, - BsonDocument fields, - IElementNameValidator queryValidator, - int skip, - int batchSize, - bool secondaryOk, - bool partialOk, - bool noCursorTimeout, - bool oplogReplay, - bool tailableCursor, - bool awaitData, - IBsonSerializer serializer, - MessageEncoderSettings messageEncoderSettings, - CancellationToken cancellationToken) - { - secondaryOk = GetEffectiveSecondaryOk(secondaryOk); -#pragma warning disable 618 - var protocol = new QueryWireProtocol( - collectionNamespace, - query, - fields, - queryValidator, - skip, - batchSize, - secondaryOk, - partialOk, - noCursorTimeout, - oplogReplay, - tailableCursor, - awaitData, - serializer, - messageEncoderSettings); -#pragma warning restore 618 - - return ExecuteProtocolAsync(protocol, cancellationToken); - } - private ICoreSession CreateClusterClockAdvancingCoreSession(ICoreSession session) { return new ClusterClockAdvancingCoreSession(session, _server.ClusterClock); } - private TResult ExecuteProtocol(IWireProtocol protocol, CancellationToken cancellationToken) - { - try - { - return protocol.Execute(_connection, cancellationToken); - } - catch (Exception ex) - { - _server.HandleChannelException(_connection, ex); - throw; - } - } - private TResult ExecuteProtocol(IWireProtocol protocol, ICoreSession session, CancellationToken cancellationToken) { try @@ -563,19 +398,6 @@ private TResult ExecuteProtocol(IWireProtocol protocol, ICoreS } } - private async Task ExecuteProtocolAsync(IWireProtocol protocol, CancellationToken cancellationToken) - { - try - { - return await protocol.ExecuteAsync(_connection, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - _server.HandleChannelException(_connection, ex); - throw; - } - } - private async Task ExecuteProtocolAsync(IWireProtocol protocol, ICoreSession session, CancellationToken cancellationToken) { try @@ -597,16 +419,6 @@ public IChannelHandle Fork() return new ServerChannel(_server, _connection.Fork(), false); } - private bool GetEffectiveSecondaryOk(bool secondaryOk) - { - if (_server.DirectConnection && _server.Description.Type != ServerType.ShardRouter) - { - return true; - } - - return secondaryOk; - } - private void MarkSessionDirtyIfNeeded(ICoreSession session, Exception ex) { if (ex is MongoConnectionException) diff --git a/src/MongoDB.Driver/Core/WireProtocol/QueryWireProtocol.cs b/src/MongoDB.Driver/Core/WireProtocol/QueryWireProtocol.cs deleted file mode 100644 index 405fb21608f..00000000000 --- a/src/MongoDB.Driver/Core/WireProtocol/QueryWireProtocol.cs +++ /dev/null @@ -1,182 +0,0 @@ -/* Copyright 2010-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Threading; -using System.Threading.Tasks; -using MongoDB.Bson; -using MongoDB.Bson.IO; -using MongoDB.Bson.Serialization; -using MongoDB.Driver.Core.Connections; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.WireProtocol.Messages; -using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; - -namespace MongoDB.Driver.Core.WireProtocol -{ - internal sealed class QueryWireProtocol : IWireProtocol> - { - // fields - private readonly bool _awaitData; - private readonly int _batchSize; - private readonly CollectionNamespace _collectionNamespace; - private readonly MessageEncoderSettings _messageEncoderSettings; - private readonly BsonDocument _fields; - private readonly bool _noCursorTimeout; - private readonly bool _oplogReplay; - private readonly bool _partialOk; - private readonly BsonDocument _query; - private readonly IElementNameValidator _queryValidator; - private readonly IBsonSerializer _serializer; - private readonly int _skip; - private readonly bool _secondaryOk; - private readonly bool _tailableCursor; - - // constructors - public QueryWireProtocol( - CollectionNamespace collectionNamespace, - BsonDocument query, - BsonDocument fields, - IElementNameValidator queryValidator, - int skip, - int batchSize, - bool secondaryOk, - bool partialOk, - bool noCursorTimeout, - bool tailableCursor, - bool awaitData, - IBsonSerializer serializer, - MessageEncoderSettings messageEncoderSettings) -#pragma warning disable 618 - : this( - collectionNamespace, - query, - fields, - queryValidator, - skip, - batchSize, - secondaryOk, - partialOk, - noCursorTimeout, - oplogReplay: false, - tailableCursor, - awaitData, - serializer, - messageEncoderSettings) -#pragma warning restore 618 - { - } - - [Obsolete("Use a constructor that does not have an oplogReplay parameter instead.")] - public QueryWireProtocol( - CollectionNamespace collectionNamespace, - BsonDocument query, - BsonDocument fields, - IElementNameValidator queryValidator, - int skip, - int batchSize, - bool secondaryOk, - bool partialOk, - bool noCursorTimeout, - bool oplogReplay, // obsolete: OplogReplay is ignored by server versions 4.4.0 and newer - bool tailableCursor, - bool awaitData, - IBsonSerializer serializer, - MessageEncoderSettings messageEncoderSettings) - { - _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); - _query = Ensure.IsNotNull(query, nameof(query)); - _fields = fields; // can be null - _queryValidator = Ensure.IsNotNull(queryValidator, nameof(queryValidator)); - _skip = Ensure.IsGreaterThanOrEqualToZero(skip, nameof(skip)); - _batchSize = batchSize; // can be negative - _secondaryOk = secondaryOk; - _partialOk = partialOk; - _noCursorTimeout = noCursorTimeout; - _oplogReplay = oplogReplay; - _tailableCursor = tailableCursor; - _awaitData = awaitData; - _serializer = Ensure.IsNotNull(serializer, nameof(serializer)); - _messageEncoderSettings = messageEncoderSettings; - } - - // public properties - public bool MoreToCome => false; - - // methods - private QueryMessage CreateMessage() - { -#pragma warning disable 618 - return new QueryMessage( - RequestMessage.GetNextRequestId(), - _collectionNamespace, - _query, - _fields, - _queryValidator, - _skip, - _batchSize, - _secondaryOk, - _partialOk, - _noCursorTimeout, - _oplogReplay, - _tailableCursor, - _awaitData); -#pragma warning restore 618 - } - - public CursorBatch Execute(IConnection connection, CancellationToken cancellationToken) - { - var message = CreateMessage(); - connection.SendMessage(message, _messageEncoderSettings, cancellationToken); - var encoderSelector = new ReplyMessageEncoderSelector(_serializer); - var reply = connection.ReceiveMessage(message.RequestId, encoderSelector, _messageEncoderSettings, cancellationToken); - return ProcessReply(connection.ConnectionId, (ReplyMessage)reply); - } - - public async Task> ExecuteAsync(IConnection connection, CancellationToken cancellationToken) - { - var message = CreateMessage(); - await connection.SendMessageAsync(message, _messageEncoderSettings, cancellationToken).ConfigureAwait(false); - var encoderSelector = new ReplyMessageEncoderSelector(_serializer); - var reply = await connection.ReceiveMessageAsync(message.RequestId, encoderSelector, _messageEncoderSettings, cancellationToken).ConfigureAwait(false); - return ProcessReply(connection.ConnectionId, (ReplyMessage)reply); - } - - private CursorBatch ProcessReply(ConnectionId connectionId, ReplyMessage reply) - { - if (reply.QueryFailure) - { - var response = reply.QueryFailureDocument; - - var notPrimaryOrNodeIsRecoveringException = ExceptionMapper.MapNotPrimaryOrNodeIsRecovering(connectionId, _query, response, "$err"); - if (notPrimaryOrNodeIsRecoveringException != null) - { - throw notPrimaryOrNodeIsRecoveringException; - } - - var mappedException = ExceptionMapper.Map(connectionId, response); - if (mappedException != null) - { - throw mappedException; - } - - var message = string.Format("QueryFailure flag was true (response was {0}).", response.ToJson()); - throw new MongoQueryException(connectionId, message, _query, response); - } - - return new CursorBatch(reply.CursorId, reply.Documents); - } - } -} From 73c8d10554e4c8867e1c2dcd834092f35655303b Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Wed, 18 Jun 2025 11:31:51 -0700 Subject: [PATCH 23/24] CSHARP-5618: CSOT: Race condition in ExclusiveConnectionPool could lead to ArgumentOutOfRangeException (#1712) --- src/MongoDB.Driver/OperationContext.cs | 16 +++++--- .../Threading/ThreadingUtilities.cs | 2 +- .../OperationContextTests.cs | 41 +++++++++++++++---- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/MongoDB.Driver/OperationContext.cs b/src/MongoDB.Driver/OperationContext.cs index d9c25405e24..c0ccd67919f 100644 --- a/src/MongoDB.Driver/OperationContext.cs +++ b/src/MongoDB.Driver/OperationContext.cs @@ -17,9 +17,7 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -#if !NET6_0_OR_GREATER using MongoDB.Driver.Core.Misc; -#endif namespace MongoDB.Driver { @@ -36,7 +34,7 @@ public OperationContext(TimeSpan timeout, CancellationToken cancellationToken) internal OperationContext(Stopwatch stopwatch, TimeSpan timeout, CancellationToken cancellationToken) { Stopwatch = stopwatch; - Timeout = timeout; + Timeout = Ensure.IsInfiniteOrGreaterThanOrEqualToZero(timeout, nameof(timeout)); CancellationToken = cancellationToken; } @@ -53,7 +51,13 @@ public TimeSpan RemainingTimeout return System.Threading.Timeout.InfiniteTimeSpan; } - return Timeout - Stopwatch.Elapsed; + var result = Timeout - Stopwatch.Elapsed; + if (result < TimeSpan.Zero) + { + result = TimeSpan.Zero; + } + + return result; } } @@ -69,7 +73,7 @@ public bool IsTimedOut() return false; } - return remainingTimeout < TimeSpan.Zero; + return remainingTimeout == TimeSpan.Zero; } public void ThrowIfTimedOutOrCanceled() @@ -141,6 +145,8 @@ public async Task WaitTaskAsync(Task task) public OperationContext WithTimeout(TimeSpan timeout) { + Ensure.IsInfiniteOrGreaterThanOrEqualToZero(timeout, nameof(timeout)); + var remainingTimeout = RemainingTimeout; if (timeout == System.Threading.Timeout.InfiniteTimeSpan) { diff --git a/tests/MongoDB.Bson.TestHelpers/Threading/ThreadingUtilities.cs b/tests/MongoDB.Bson.TestHelpers/Threading/ThreadingUtilities.cs index b0b97b6b809..a7cd44e5ce4 100644 --- a/tests/MongoDB.Bson.TestHelpers/Threading/ThreadingUtilities.cs +++ b/tests/MongoDB.Bson.TestHelpers/Threading/ThreadingUtilities.cs @@ -29,7 +29,7 @@ public static void ExecuteOnNewThreads(int threadsCount, Action action, int if (exceptions.Any()) { - throw exceptions.First(); + throw new AggregateException(exceptions); } } diff --git a/tests/MongoDB.Driver.Tests/OperationContextTests.cs b/tests/MongoDB.Driver.Tests/OperationContextTests.cs index 9b253837d8a..49644802605 100644 --- a/tests/MongoDB.Driver.Tests/OperationContextTests.cs +++ b/tests/MongoDB.Driver.Tests/OperationContextTests.cs @@ -42,6 +42,14 @@ public void Constructor_should_initialize_properties() operationContext.ParentContext.Should().BeNull(); } + [Fact] + public void Constructor_throws_on_negative_timeout() + { + var exception = Record.Exception(() => new OperationContext(TimeSpan.FromSeconds(-5), CancellationToken.None)); + + exception.Should().BeOfType(); + } + [Fact] public void RemainingTimeout_should_calculate() { @@ -68,16 +76,12 @@ public void RemainingTimeout_should_return_infinite_for_infinite_timeout() } [Fact] - public void RemainingTimeout_could_be_negative() + public void RemainingTimeout_should_return_zero_for_timeout_context() { - var timeout = TimeSpan.FromMilliseconds(5); - var stopwatch = Stopwatch.StartNew(); + var operationContext = new OperationContext(TimeSpan.FromMilliseconds(5), CancellationToken.None); Thread.Sleep(10); - stopwatch.Stop(); - var operationContext = new OperationContext(stopwatch, timeout, CancellationToken.None); - - operationContext.RemainingTimeout.Should().Be(timeout - stopwatch.Elapsed); + operationContext.RemainingTimeout.Should().Be(TimeSpan.Zero); } [Theory] @@ -276,6 +280,29 @@ public void WithTimeout_should_set_ParentContext() resultContext.ParentContext.Should().Be(operationContext); } + + [Fact] + public void WithTimeout_should_create_timed_out_context_on_timed_out_context() + { + var operationContext = new OperationContext(TimeSpan.FromMilliseconds(5), CancellationToken.None); + Thread.Sleep(10); + operationContext.IsTimedOut().Should().BeTrue(); + + var resultContext = operationContext.WithTimeout(TimeSpan.FromSeconds(10)); + + resultContext.IsTimedOut().Should().BeTrue(); + } + + [Fact] + public void WithTimeout_throws_on_negative_timeout() + { + var operationContext = new OperationContext(Timeout.InfiniteTimeSpan, CancellationToken.None); + + var exception = Record.Exception(() => operationContext.WithTimeout(TimeSpan.FromSeconds(-5))); + + exception.Should().BeOfType() + .Subject.ParamName.Should().Be("timeout"); + } } } From 79fcf06f6c600ae5f0edbb1bc1352f9c4b733db5 Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Wed, 18 Jun 2025 12:45:57 -0700 Subject: [PATCH 24/24] CSHARP-5619: Replace IConnection.SendMessages with the method to send a single message (#1713) --- .../ExclusiveConnectionPool.Helpers.cs | 18 +- .../Core/Connections/BinaryConnection.cs | 136 ++++++------- .../Core/Connections/CommandEventHelper.cs | 60 +++--- .../Core/Connections/ConnectionExtensions.cs | 39 ---- .../Core/Connections/IConnection.cs | 4 +- .../Events/ConnectionSendingMessagesEvent.cs | 31 ++- .../ConnectionSendingMessagesFailedEvent.cs | 33 +++- .../Events/ConnectionSentMessagesEvent.cs | 37 +++- .../PerformanceCounterEventSubscriber.cs | 7 +- .../Diagnostics/TraceSourceEventSubscriber.cs | 6 +- .../CommandMessageFieldEncryptor.cs | 2 +- .../CommandUsingCommandMessageWireProtocol.cs | 3 +- .../Messages/CommandRequestMessage.cs | 4 +- .../CommandRequestMessageBinaryEncoder.cs | 2 +- .../CommandRequestMessageJsonEncoder.cs | 2 +- .../WireProtocol/Messages/QueryMessage.cs | 11 +- .../WireProtocol/Messages/RequestMessage.cs | 10 +- .../WireProtocol/WriteWireProtocolBase.cs | 185 ------------------ .../Core/MockConnection.cs | 12 +- .../Core/Connections/BinaryConnectionTests.cs | 66 +++---- .../BinaryConnection_CommandEventTests.cs | 50 +++-- .../Core/Helpers/MessageHelper.cs | 2 +- .../Messages/CommandRequestMessageTests.cs | 14 +- ...CommandRequestMessageBinaryEncoderTests.cs | 2 +- .../QueryMessageBinaryEncoderTests.cs | 4 +- .../CommandRequestMessageJsonEncoderTests.cs | 2 +- 26 files changed, 266 insertions(+), 476 deletions(-) delete mode 100644 src/MongoDB.Driver/Core/Connections/ConnectionExtensions.cs delete mode 100644 src/MongoDB.Driver/Core/WireProtocol/WriteWireProtocolBase.cs diff --git a/src/MongoDB.Driver/Core/ConnectionPools/ExclusiveConnectionPool.Helpers.cs b/src/MongoDB.Driver/Core/ConnectionPools/ExclusiveConnectionPool.Helpers.cs index b4adb83a8db..5ff4a0f0845 100644 --- a/src/MongoDB.Driver/Core/ConnectionPools/ExclusiveConnectionPool.Helpers.cs +++ b/src/MongoDB.Driver/Core/ConnectionPools/ExclusiveConnectionPool.Helpers.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-present MongoDB Inc. +/* Copyright 2010-present MongoDB Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -461,11 +461,11 @@ public async Task ReceiveMessageAsync(int responseTo, IMessageE } } - public void SendMessages(IEnumerable messages, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) + public void SendMessage(RequestMessage message, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) { try { - _connection.SendMessages(messages, messageEncoderSettings, cancellationToken); + _connection.SendMessage(message, messageEncoderSettings, cancellationToken); } catch (MongoConnectionException ex) { @@ -474,11 +474,11 @@ public void SendMessages(IEnumerable messages, MessageEncoderSet } } - public async Task SendMessagesAsync(IEnumerable messages, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) + public async Task SendMessageAsync(RequestMessage message, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) { try { - await _connection.SendMessagesAsync(messages, messageEncoderSettings, cancellationToken).ConfigureAwait(false); + await _connection.SendMessageAsync(message, messageEncoderSettings, cancellationToken).ConfigureAwait(false); } catch (MongoConnectionException ex) { @@ -623,16 +623,16 @@ public ResponseMessage ReceiveMessage(int responseTo, IMessageEncoderSelector en return _reference.Instance.ReceiveMessage(responseTo, encoderSelector, messageEncoderSettings, cancellationToken); } - public void SendMessages(IEnumerable messages, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) + public void SendMessage(RequestMessage message, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) { ThrowIfDisposed(); - _reference.Instance.SendMessages(messages, messageEncoderSettings, cancellationToken); + _reference.Instance.SendMessage(message, messageEncoderSettings, cancellationToken); } - public Task SendMessagesAsync(IEnumerable messages, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) + public Task SendMessageAsync(RequestMessage message, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) { ThrowIfDisposed(); - return _reference.Instance.SendMessagesAsync(messages, messageEncoderSettings, cancellationToken); + return _reference.Instance.SendMessageAsync(message, messageEncoderSettings, cancellationToken); } public void SetCheckOutReasonIfNotAlreadySet(CheckOutReason reason) diff --git a/src/MongoDB.Driver/Core/Connections/BinaryConnection.cs b/src/MongoDB.Driver/Core/Connections/BinaryConnection.cs index d026da63a5e..210e33cc14c 100644 --- a/src/MongoDB.Driver/Core/Connections/BinaryConnection.cs +++ b/src/MongoDB.Driver/Core/Connections/BinaryConnection.cs @@ -1,4 +1,4 @@ -/* Copyright 2013-present MongoDB Inc. +/* Copyright 2010-present MongoDB Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,8 @@ using System; using System.Buffers.Binary; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; @@ -582,22 +580,22 @@ private async Task SendBufferAsync(IByteBuffer buffer, CancellationToken cancell } } - public void SendMessages(IEnumerable messages, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) + public void SendMessage(RequestMessage message, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) { - Ensure.IsNotNull(messages, nameof(messages)); + Ensure.IsNotNull(message, nameof(message)); ThrowIfCancelledOrDisposedOrNotOpen(cancellationToken); - var helper = new SendMessagesHelper(this, messages, messageEncoderSettings); + var helper = new SendMessageHelper(this, message, messageEncoderSettings); try { - helper.EncodingMessages(); - using (var uncompressedBuffer = helper.EncodeMessages(cancellationToken, out var sentMessages)) + helper.EncodingMessage(); + using (var uncompressedBuffer = helper.EncodeMessage(cancellationToken, out var sentMessage)) { - helper.SendingMessages(uncompressedBuffer); + helper.SendingMessage(uncompressedBuffer); int sentLength; - if (AnyMessageNeedsToBeCompressed(sentMessages)) + if (ShouldBeCompressed(sentMessage)) { - using (var compressedBuffer = CompressMessages(sentMessages, uncompressedBuffer, messageEncoderSettings)) + using (var compressedBuffer = CompressMessage(sentMessage, uncompressedBuffer, messageEncoderSettings)) { SendBuffer(compressedBuffer, cancellationToken); sentLength = compressedBuffer.Length; @@ -608,33 +606,33 @@ public void SendMessages(IEnumerable messages, MessageEncoderSet SendBuffer(uncompressedBuffer, cancellationToken); sentLength = uncompressedBuffer.Length; } - helper.SentMessages(sentLength); + helper.SentMessage(sentLength); } } catch (Exception ex) { - helper.FailedSendingMessages(ex); + helper.FailedSendingMessage(ex); ThrowOperationCanceledExceptionIfRequired(ex); throw; } } - public async Task SendMessagesAsync(IEnumerable messages, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) + public async Task SendMessageAsync(RequestMessage message, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) { - Ensure.IsNotNull(messages, nameof(messages)); + Ensure.IsNotNull(message, nameof(message)); ThrowIfCancelledOrDisposedOrNotOpen(cancellationToken); - var helper = new SendMessagesHelper(this, messages, messageEncoderSettings); + var helper = new SendMessageHelper(this, message, messageEncoderSettings); try { - helper.EncodingMessages(); - using (var uncompressedBuffer = helper.EncodeMessages(cancellationToken, out var sentMessages)) + helper.EncodingMessage(); + using (var uncompressedBuffer = helper.EncodeMessage(cancellationToken, out var sentMessage)) { - helper.SendingMessages(uncompressedBuffer); + helper.SendingMessage(uncompressedBuffer); int sentLength; - if (AnyMessageNeedsToBeCompressed(sentMessages)) + if (ShouldBeCompressed(sentMessage)) { - using (var compressedBuffer = CompressMessages(sentMessages, uncompressedBuffer, messageEncoderSettings)) + using (var compressedBuffer = CompressMessage(sentMessage, uncompressedBuffer, messageEncoderSettings)) { await SendBufferAsync(compressedBuffer, cancellationToken).ConfigureAwait(false); sentLength = compressedBuffer.Length; @@ -645,12 +643,12 @@ public async Task SendMessagesAsync(IEnumerable messages, Messag await SendBufferAsync(uncompressedBuffer, cancellationToken).ConfigureAwait(false); sentLength = uncompressedBuffer.Length; } - helper.SentMessages(sentLength); + helper.SentMessage(sentLength); } } catch (Exception ex) { - helper.FailedSendingMessages(ex); + helper.FailedSendingMessage(ex); ThrowOperationCanceledExceptionIfRequired(ex); throw; } @@ -663,9 +661,9 @@ public void SetReadTimeout(TimeSpan timeout) } // private methods - private bool AnyMessageNeedsToBeCompressed(IEnumerable messages) + private bool ShouldBeCompressed(RequestMessage message) { - return _sendCompressorType.HasValue && messages.Any(m => m.MayBeCompressed); + return _sendCompressorType.HasValue && message.MayBeCompressed; } private CompressorType? ChooseSendCompressorTypeIfAny(ConnectionDescription connectionDescription) @@ -674,8 +672,8 @@ private bool AnyMessageNeedsToBeCompressed(IEnumerable messages) return availableCompressors.Count > 0 ? (CompressorType?)availableCompressors[0] : null; } - private IByteBuffer CompressMessages( - IEnumerable messages, + private IByteBuffer CompressMessage( + RequestMessage message, IByteBuffer uncompressedBuffer, MessageEncoderSettings messageEncoderSettings) { @@ -685,24 +683,22 @@ private IByteBuffer CompressMessages( using (var uncompressedStream = new ByteBufferStream(uncompressedBuffer, ownsBuffer: false)) using (var compressedStream = new ByteBufferStream(compressedBuffer, ownsBuffer: false)) { - foreach (var message in messages) - { - var uncompressedMessageLength = uncompressedStream.ReadInt32(); - uncompressedStream.Position -= 4; + var uncompressedMessageLength = uncompressedStream.ReadInt32(); + uncompressedStream.Position -= 4; - using (var uncompressedMessageSlice = uncompressedBuffer.GetSlice((int)uncompressedStream.Position, uncompressedMessageLength)) - using (var uncompressedMessageStream = new ByteBufferStream(uncompressedMessageSlice, ownsBuffer: false)) + using (var uncompressedMessageSlice = uncompressedBuffer.GetSlice((int)uncompressedStream.Position, uncompressedMessageLength)) + using (var uncompressedMessageStream = new ByteBufferStream(uncompressedMessageSlice, ownsBuffer: false)) + { + if (message.MayBeCompressed) { - if (message.MayBeCompressed) - { - CompressMessage(message, uncompressedMessageStream, compressedStream, messageEncoderSettings); - } - else - { - uncompressedMessageStream.EfficientCopyTo(compressedStream); - } + CompressMessage(message, uncompressedMessageStream, compressedStream, messageEncoderSettings); + } + else + { + uncompressedMessageStream.EfficientCopyTo(compressedStream); } } + compressedBuffer.Length = (int)compressedStream.Length; } @@ -978,29 +974,27 @@ private Opcode PeekOpcode(BsonStream stream) } } - private class SendMessagesHelper + private class SendMessageHelper { private readonly Stopwatch _commandStopwatch; private readonly BinaryConnection _connection; private readonly MessageEncoderSettings _messageEncoderSettings; - private readonly List _messages; - private Lazy> _requestIds; + private readonly RequestMessage _message; private TimeSpan _serializationDuration; private Stopwatch _networkStopwatch; - public SendMessagesHelper(BinaryConnection connection, IEnumerable messages, MessageEncoderSettings messageEncoderSettings) + public SendMessageHelper(BinaryConnection connection, RequestMessage message, MessageEncoderSettings messageEncoderSettings) { _connection = connection; - _messages = messages.ToList(); + _message = message; _messageEncoderSettings = messageEncoderSettings; _commandStopwatch = Stopwatch.StartNew(); - _requestIds = new Lazy>(() => _messages.Select(m => m.RequestId).ToList()); } - public IByteBuffer EncodeMessages(CancellationToken cancellationToken, out List sentMessages) + public IByteBuffer EncodeMessage(CancellationToken cancellationToken, out RequestMessage sentMessage) { - sentMessages = new List(); + sentMessage = null; cancellationToken.ThrowIfCancellationRequested(); var serializationStopwatch = Stopwatch.StartNew(); @@ -1009,21 +1003,17 @@ public IByteBuffer EncodeMessages(CancellationToken cancellationToken, out List< using (var stream = new ByteBufferStream(buffer, ownsBuffer: false)) { var encoderFactory = new BinaryMessageEncoderFactory(stream, _messageEncoderSettings, compressorSource: null); - foreach (var message in _messages) - { - if (message.ShouldBeSent == null || message.ShouldBeSent()) - { - var encoder = message.GetEncoder(encoderFactory); - encoder.WriteMessage(message); - message.WasSent = true; - sentMessages.Add(message); - } - // Encoding messages includes serializing the - // documents, so encoding message could be expensive - // and worthy of us honoring cancellation here. - cancellationToken.ThrowIfCancellationRequested(); - } + var encoder = _message.GetEncoder(encoderFactory); + encoder.WriteMessage(_message); + _message.WasSent = true; + sentMessage = _message; + + // Encoding messages includes serializing the + // documents, so encoding message could be expensive + // and worthy of us honoring cancellation here. + cancellationToken.ThrowIfCancellationRequested(); + buffer.Length = (int)stream.Length; buffer.MakeReadOnly(); } @@ -1033,42 +1023,42 @@ public IByteBuffer EncodeMessages(CancellationToken cancellationToken, out List< return buffer; } - public void EncodingMessages() + public void EncodingMessage() { - _connection._eventLogger.LogAndPublish(new ConnectionSendingMessagesEvent(_connection.ConnectionId, _requestIds.Value, EventContext.OperationId)); + _connection._eventLogger.LogAndPublish(new ConnectionSendingMessagesEvent(_connection.ConnectionId, _message.RequestId, EventContext.OperationId)); } - public void FailedSendingMessages(Exception ex) + public void FailedSendingMessage(Exception ex) { if (_connection._commandEventHelper.ShouldCallErrorSending) { - _connection._commandEventHelper.ErrorSending(_messages, _connection._connectionId, _connection._description?.ServiceId, ex, _connection.IsInitializing); + _connection._commandEventHelper.ErrorSending(_message, _connection._connectionId, _connection._description?.ServiceId, ex, _connection.IsInitializing); } - _connection._eventLogger.LogAndPublish(new ConnectionSendingMessagesFailedEvent(_connection.ConnectionId, _requestIds.Value, ex, EventContext.OperationId)); + _connection._eventLogger.LogAndPublish(new ConnectionSendingMessagesFailedEvent(_connection.ConnectionId, _message.RequestId, ex, EventContext.OperationId)); } - public void SendingMessages(IByteBuffer buffer) + public void SendingMessage(IByteBuffer buffer) { if (_connection._commandEventHelper.ShouldCallBeforeSending) { - _connection._commandEventHelper.BeforeSending(_messages, _connection.ConnectionId, _connection.Description?.ServiceId, buffer, _messageEncoderSettings, _commandStopwatch, _connection.IsInitializing); + _connection._commandEventHelper.BeforeSending(_message, _connection.ConnectionId, _connection.Description?.ServiceId, buffer, _messageEncoderSettings, _commandStopwatch, _connection.IsInitializing); } _networkStopwatch = Stopwatch.StartNew(); } - public void SentMessages(int bufferLength) + public void SentMessage(int bufferLength) { _networkStopwatch.Stop(); var networkDuration = _networkStopwatch.Elapsed; if (_connection._commandEventHelper.ShouldCallAfterSending) { - _connection._commandEventHelper.AfterSending(_messages, _connection._connectionId, _connection.Description?.ServiceId, _connection.IsInitializing); + _connection._commandEventHelper.AfterSending(_message, _connection._connectionId, _connection.Description?.ServiceId, _connection.IsInitializing); } - _connection._eventLogger.LogAndPublish(new ConnectionSentMessagesEvent(_connection.ConnectionId, _requestIds.Value, bufferLength, networkDuration, _serializationDuration, EventContext.OperationId)); + _connection._eventLogger.LogAndPublish(new ConnectionSentMessagesEvent(_connection.ConnectionId, _message.RequestId, bufferLength, networkDuration, _serializationDuration, EventContext.OperationId)); } } diff --git a/src/MongoDB.Driver/Core/Connections/CommandEventHelper.cs b/src/MongoDB.Driver/Core/Connections/CommandEventHelper.cs index 5b64bf2a545..1678c55c273 100644 --- a/src/MongoDB.Driver/Core/Connections/CommandEventHelper.cs +++ b/src/MongoDB.Driver/Core/Connections/CommandEventHelper.cs @@ -1,4 +1,4 @@ -/* Copyright 2015-present MongoDB Inc. +/* Copyright 2010-present MongoDB Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -83,7 +82,7 @@ public bool ShouldCallErrorReceiving } public void BeforeSending( - IEnumerable messages, + RequestMessage message, ConnectionId connectionId, ObjectId? serviceId, IByteBuffer buffer, @@ -93,28 +92,21 @@ public void BeforeSending( { using (var stream = new ByteBufferStream(buffer, ownsBuffer: false)) { - var messageQueue = new Queue(messages); - - while (messageQueue.Count > 0) - { - ProcessRequestMessages(messageQueue, connectionId, serviceId, stream, encoderSettings, stopwatch, skipLogging); - } + ProcessRequestMessage(message, connectionId, serviceId, stream, encoderSettings, stopwatch, skipLogging); } } - public void AfterSending(IEnumerable messages, ConnectionId connectionId, ObjectId? serviceId, bool skipLogging) + public void AfterSending(RequestMessage message, ConnectionId connectionId, ObjectId? serviceId, bool skipLogging) { - foreach (var message in messages) + CommandState state; + if (_state.TryGetValue(message.RequestId, out state) && + state.ExpectedResponseType == ExpectedResponseType.None) { - CommandState state; - if (_state.TryGetValue(message.RequestId, out state) && - state.ExpectedResponseType == ExpectedResponseType.None) - { - state.Stopwatch.Stop(); + state.Stopwatch.Stop(); - if (_shouldTrackSucceeded) - { - _eventLogger.LogAndPublish(new CommandSucceededEvent( + if (_shouldTrackSucceeded) + { + _eventLogger.LogAndPublish(new CommandSucceededEvent( state.CommandName, new BsonDocument("ok", 1), state.QueryNamespace.DatabaseNamespace, @@ -123,22 +115,20 @@ public void AfterSending(IEnumerable messages, ConnectionId conn connectionId, serviceId, state.Stopwatch.Elapsed), - skipLogging); - } - _state.TryRemove(message.RequestId, out state); + skipLogging); } + + _state.TryRemove(message.RequestId, out state); } } - public void ErrorSending(IEnumerable messages, ConnectionId connectionId, ObjectId? serviceId, Exception exception, bool skipLogging) + public void ErrorSending(RequestMessage message, ConnectionId connectionId, ObjectId? serviceId, Exception exception, bool skipLogging) { - foreach (var message in messages) + CommandState state; + if (_state.TryRemove(message.RequestId, out state)) { - CommandState state; - if (_state.TryRemove(message.RequestId, out state)) - { - state.Stopwatch.Stop(); - _eventLogger.LogAndPublish(new CommandFailedEvent( + state.Stopwatch.Stop(); + _eventLogger.LogAndPublish(new CommandFailedEvent( state.CommandName, state.QueryNamespace.DatabaseNamespace, exception, @@ -147,8 +137,7 @@ public void ErrorSending(IEnumerable messages, ConnectionId conn connectionId, serviceId, state.Stopwatch.Elapsed), - skipLogging); - } + skipLogging); } } @@ -222,13 +211,12 @@ public void ConnectionFailed(ConnectionId connectionId, ObjectId? serviceId, Exc } } - private void ProcessRequestMessages(Queue messageQueue, ConnectionId connectionId, ObjectId? serviceId, Stream stream, MessageEncoderSettings encoderSettings, Stopwatch stopwatch, bool skipLogging) + private void ProcessRequestMessage(RequestMessage message, ConnectionId connectionId, ObjectId? serviceId, Stream stream, MessageEncoderSettings encoderSettings, Stopwatch stopwatch, bool skipLogging) { - var message = messageQueue.Dequeue(); switch (message.MessageType) { case MongoDBMessageType.Command: - ProcessCommandRequestMessage((CommandRequestMessage)message, messageQueue, connectionId, serviceId, new CommandMessageBinaryEncoder(stream, encoderSettings), stopwatch, skipLogging); + ProcessCommandRequestMessage((CommandRequestMessage)message, connectionId, serviceId, new CommandMessageBinaryEncoder(stream, encoderSettings), stopwatch, skipLogging); break; case MongoDBMessageType.Query: ProcessQueryMessage((QueryMessage)message, connectionId, new QueryMessageBinaryEncoder(stream, encoderSettings), stopwatch, skipLogging); @@ -238,9 +226,9 @@ private void ProcessRequestMessages(Queue messageQueue, Connecti } } - private void ProcessCommandRequestMessage(CommandRequestMessage originalMessage, Queue messageQueue, ConnectionId connectionId, ObjectId? serviceId, CommandMessageBinaryEncoder encoder, Stopwatch stopwatch, bool skipLogging) + private void ProcessCommandRequestMessage(CommandRequestMessage message, ConnectionId connectionId, ObjectId? serviceId, CommandMessageBinaryEncoder encoder, Stopwatch stopwatch, bool skipLogging) { - var requestId = originalMessage.RequestId; + var requestId = message.RequestId; var operationId = EventContext.OperationId; var decodedMessage = encoder.ReadMessage(); diff --git a/src/MongoDB.Driver/Core/Connections/ConnectionExtensions.cs b/src/MongoDB.Driver/Core/Connections/ConnectionExtensions.cs deleted file mode 100644 index 3b6b0bcb086..00000000000 --- a/src/MongoDB.Driver/Core/Connections/ConnectionExtensions.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright 2010-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System.Threading; -using System.Threading.Tasks; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.WireProtocol.Messages; -using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; - -namespace MongoDB.Driver.Core.Connections -{ - internal static class ConnectionExtensions - { - // static methods - public static void SendMessage(this IConnection connection, RequestMessage message, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) - { - Ensure.IsNotNull(connection, nameof(connection)); - connection.SendMessages(new[] { message }, messageEncoderSettings, cancellationToken); - } - - public static Task SendMessageAsync(this IConnection connection, RequestMessage message, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) - { - Ensure.IsNotNull(connection, nameof(connection)); - return connection.SendMessagesAsync(new[] { message }, messageEncoderSettings, cancellationToken); - } - } -} diff --git a/src/MongoDB.Driver/Core/Connections/IConnection.cs b/src/MongoDB.Driver/Core/Connections/IConnection.cs index 961def30f29..a82dfc3eda3 100644 --- a/src/MongoDB.Driver/Core/Connections/IConnection.cs +++ b/src/MongoDB.Driver/Core/Connections/IConnection.cs @@ -40,8 +40,8 @@ internal interface IConnection : IDisposable Task ReauthenticateAsync(CancellationToken cancellationToken); ResponseMessage ReceiveMessage(int responseTo, IMessageEncoderSelector encoderSelector, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken); Task ReceiveMessageAsync(int responseTo, IMessageEncoderSelector encoderSelector, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken); - void SendMessages(IEnumerable messages, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken); - Task SendMessagesAsync(IEnumerable messages, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken); + void SendMessage(RequestMessage message, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken); + Task SendMessageAsync(RequestMessage message, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken); } internal interface IConnectionHandle : IConnection diff --git a/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesEvent.cs b/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesEvent.cs index 1e80c9a8e66..f61acad279d 100644 --- a/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesEvent.cs +++ b/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesEvent.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; +using System.Linq; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Servers; @@ -28,7 +29,7 @@ public struct ConnectionSendingMessagesEvent : IEvent { private readonly ConnectionId _connectionId; private readonly long? _operationId; - private readonly IReadOnlyList _requestIds; + private readonly int _requestId; private readonly DateTime _timestamp; /// @@ -37,10 +38,25 @@ public struct ConnectionSendingMessagesEvent : IEvent /// The connection identifier. /// The request ids. /// The operation identifier. + [Obsolete("Support for sending multiple messages has been removed, use the constructor with single requestId instead.")] public ConnectionSendingMessagesEvent(ConnectionId connectionId, IReadOnlyList requestIds, long? operationId) { _connectionId = connectionId; - _requestIds = requestIds; + _requestId = requestIds.Single(); + _operationId = operationId; + _timestamp = DateTime.UtcNow; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The connection identifier. + /// The request id. + /// The operation identifier. + public ConnectionSendingMessagesEvent(ConnectionId connectionId, int requestId, long? operationId) + { + _connectionId = connectionId; + _requestId = requestId; _operationId = operationId; _timestamp = DateTime.UtcNow; } @@ -61,12 +77,21 @@ public ConnectionId ConnectionId get { return _connectionId; } } + /// + /// Gets the request id. + /// + public int RequestId + { + get { return _requestId; } + } + /// /// Gets the request ids. /// + [Obsolete($"Support for sending multiple messages has been removed, use {nameof(RequestId)} instead.")] public IReadOnlyList RequestIds { - get { return _requestIds; } + get { return [_requestId]; } } /// diff --git a/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesFailedEvent.cs b/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesFailedEvent.cs index 2fb5aab05df..fd9f9624d01 100644 --- a/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesFailedEvent.cs +++ b/src/MongoDB.Driver/Core/Events/ConnectionSendingMessagesFailedEvent.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; +using System.Linq; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Servers; @@ -29,7 +30,7 @@ public struct ConnectionSendingMessagesFailedEvent : IEvent private readonly ConnectionId _connectionId; private readonly Exception _exception; private readonly long? _operationId; - private readonly IReadOnlyList _requestIds; + private readonly int _requestId; private readonly DateTime _timestamp; /// @@ -39,10 +40,27 @@ public struct ConnectionSendingMessagesFailedEvent : IEvent /// The request ids. /// The exception. /// The operation identifier. + [Obsolete("Support for sending multiple messages has been removed, use the constructor with single requestId instead.")] public ConnectionSendingMessagesFailedEvent(ConnectionId connectionId, IReadOnlyList requestIds, Exception exception, long? operationId) { _connectionId = connectionId; - _requestIds = requestIds; + _requestId = requestIds.Single(); + _exception = exception; + _operationId = operationId; + _timestamp = DateTime.UtcNow; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The connection identifier. + /// The request id. + /// The exception. + /// The operation identifier. + public ConnectionSendingMessagesFailedEvent(ConnectionId connectionId, int requestId, Exception exception, long? operationId) + { + _connectionId = connectionId; + _requestId = requestId; _exception = exception; _operationId = operationId; _timestamp = DateTime.UtcNow; @@ -80,12 +98,21 @@ public long? OperationId get { return _operationId; } } + /// + /// Gets the request id. + /// + public int RequestId + { + get { return _requestId; } + } + /// /// Gets the request ids. /// + [Obsolete($"Support for sending multiple messages has been removed, use {nameof(RequestId)} instead.")] public IReadOnlyList RequestIds { - get { return _requestIds; } + get { return [_requestId]; } } /// diff --git a/src/MongoDB.Driver/Core/Events/ConnectionSentMessagesEvent.cs b/src/MongoDB.Driver/Core/Events/ConnectionSentMessagesEvent.cs index a5e3608b9c9..6e66e122339 100644 --- a/src/MongoDB.Driver/Core/Events/ConnectionSentMessagesEvent.cs +++ b/src/MongoDB.Driver/Core/Events/ConnectionSentMessagesEvent.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; +using System.Linq; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Servers; @@ -31,7 +32,7 @@ public struct ConnectionSentMessagesEvent : IEvent private readonly TimeSpan _serializationDuration; private readonly int _length; private readonly long? _operationId; - private readonly IReadOnlyList _requestIds; + private readonly int _requestId; private readonly DateTime _timestamp; /// @@ -43,10 +44,31 @@ public struct ConnectionSentMessagesEvent : IEvent /// The duration of time spent on the network. /// The duration of time spent serializing the messages. /// The operation identifier. + [Obsolete("Support for sending multiple messages has been removed, use the constructor with single requestId instead.")] public ConnectionSentMessagesEvent(ConnectionId connectionId, IReadOnlyList requestIds, int length, TimeSpan networkDuration, TimeSpan serializationDuration, long? operationId) { _connectionId = connectionId; - _requestIds = requestIds; + _requestId = requestIds.Single(); + _length = length; + _networkDuration = networkDuration; + _serializationDuration = serializationDuration; + _operationId = operationId; + _timestamp = DateTime.UtcNow; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The connection identifier. + /// The request id. + /// The length. + /// The duration of time spent on the network. + /// The duration of time spent serializing the messages. + /// The operation identifier. + public ConnectionSentMessagesEvent(ConnectionId connectionId, int requestId, int length, TimeSpan networkDuration, TimeSpan serializationDuration, long? operationId) + { + _connectionId = connectionId; + _requestId = requestId; _length = length; _networkDuration = networkDuration; _serializationDuration = serializationDuration; @@ -110,12 +132,21 @@ public int Length get { return _length; } } + /// + /// Gets the request id. + /// + public int RequestId + { + get { return _requestId; } + } + /// /// Gets the request ids. /// + [Obsolete($"Support for sending multiple messages has been removed, use {nameof(RequestId)} instead.")] public IReadOnlyList RequestIds { - get { return _requestIds; } + get { return [_requestId]; } } /// diff --git a/src/MongoDB.Driver/Core/Events/Diagnostics/PerformanceCounterEventSubscriber.cs b/src/MongoDB.Driver/Core/Events/Diagnostics/PerformanceCounterEventSubscriber.cs index c7f92351285..762e8120c43 100644 --- a/src/MongoDB.Driver/Core/Events/Diagnostics/PerformanceCounterEventSubscriber.cs +++ b/src/MongoDB.Driver/Core/Events/Diagnostics/PerformanceCounterEventSubscriber.cs @@ -16,14 +16,11 @@ #if NET472 using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Net; using System.Reflection; -using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events.Diagnostics.PerformanceCounters; using MongoDB.Driver.Core.Servers; -using MongoDB.Driver.Core.WireProtocol.Messages; namespace MongoDB.Driver.Core.Events.Diagnostics { @@ -32,7 +29,7 @@ namespace MongoDB.Driver.Core.Events.Diagnostics /// public sealed class PerformanceCounterEventSubscriber : IEventSubscriber { - //static + //static /// /// Installs the performance counters. /// @@ -169,7 +166,7 @@ private void Handle(ConnectionSentMessagesEvent @event) ConnectionPerformanceRecorder recorder; if (_connectionRecorders.TryGetValue(@event.ConnectionId, out recorder)) { - recorder.PacketSent(@event.RequestIds.Count, @event.Length); + recorder.PacketSent(1, @event.Length); } } diff --git a/src/MongoDB.Driver/Core/Events/Diagnostics/TraceSourceEventSubscriber.cs b/src/MongoDB.Driver/Core/Events/Diagnostics/TraceSourceEventSubscriber.cs index fbc1ac25c16..a362daef900 100644 --- a/src/MongoDB.Driver/Core/Events/Diagnostics/TraceSourceEventSubscriber.cs +++ b/src/MongoDB.Driver/Core/Events/Diagnostics/TraceSourceEventSubscriber.cs @@ -250,17 +250,17 @@ private void Handle(ConnectionReceivingMessageFailedEvent @event) private void Handle(ConnectionSendingMessagesEvent @event) { - Debug(TraceSourceEventHelper.ConnectionIdBase + 9, "{0}: sending messages [{1}].", TraceSourceEventHelper.Label(@event.ConnectionId), string.Join(",", @event.RequestIds)); + Debug(TraceSourceEventHelper.ConnectionIdBase + 9, "{0}: sending messages [{1}].", TraceSourceEventHelper.Label(@event.ConnectionId), @event.RequestId); } private void Handle(ConnectionSentMessagesEvent @event) { - Debug(TraceSourceEventHelper.ConnectionIdBase + 10, "{0}: sent messages [{1}] of length {2} bytes in {3}ms.", TraceSourceEventHelper.Label(@event.ConnectionId), string.Join(",", @event.RequestIds), @event.Length, @event.Duration.TotalMilliseconds); + Debug(TraceSourceEventHelper.ConnectionIdBase + 10, "{0}: sent messages [{1}] of length {2} bytes in {3}ms.", TraceSourceEventHelper.Label(@event.ConnectionId), @event.RequestId, @event.Length, @event.Duration.TotalMilliseconds); } private void Handle(ConnectionSendingMessagesFailedEvent @event) { - Error(TraceSourceEventHelper.ConnectionIdBase + 11, @event.Exception, "{0}: error sending messages [{1}].", TraceSourceEventHelper.Label(@event.ConnectionId), string.Join(",", @event.RequestIds)); + Error(TraceSourceEventHelper.ConnectionIdBase + 11, @event.Exception, "{0}: error sending messages [{1}].", TraceSourceEventHelper.Label(@event.ConnectionId), @event.RequestId); } private void Debug(int id, string message, params object[] args) diff --git a/src/MongoDB.Driver/Core/WireProtocol/CommandMessageFieldEncryptor.cs b/src/MongoDB.Driver/Core/WireProtocol/CommandMessageFieldEncryptor.cs index 3e8fc8a3bc3..4b4239fb533 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/CommandMessageFieldEncryptor.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/CommandMessageFieldEncryptor.cs @@ -155,7 +155,7 @@ private CommandRequestMessage CreateEncryptedRequestMessage(CommandRequestMessag unencryptedCommandMessage.ResponseTo, encryptedSections, unencryptedCommandMessage.MoreToCome); - return new CommandRequestMessage(encryptedCommandMessage, unencryptedRequestMessage.ShouldBeSent); + return new CommandRequestMessage(encryptedCommandMessage); } private byte[] GetUnencryptedCommandBytes(CommandRequestMessage unencryptedRequestMessage) diff --git a/src/MongoDB.Driver/Core/WireProtocol/CommandUsingCommandMessageWireProtocol.cs b/src/MongoDB.Driver/Core/WireProtocol/CommandUsingCommandMessageWireProtocol.cs index 9f88a3a7001..159fe57c70c 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/CommandUsingCommandMessageWireProtocol.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/CommandUsingCommandMessageWireProtocol.cs @@ -266,9 +266,8 @@ private CommandRequestMessage CreateCommandMessage(ConnectionDescription connect PostWriteAction = _postWriteAction, ExhaustAllowed = _responseHandling == CommandResponseHandling.ExhaustAllowed, }; - var shouldBeSent = (Func)(() => true); - return new CommandRequestMessage(wrappedMessage, shouldBeSent); + return new CommandRequestMessage(wrappedMessage); } private IEnumerable CreateSections(ConnectionDescription connectionDescription) diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/CommandRequestMessage.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/CommandRequestMessage.cs index 98c2a4fb6a5..d488eeb3e8e 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/CommandRequestMessage.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/CommandRequestMessage.cs @@ -25,8 +25,8 @@ internal sealed class CommandRequestMessage : RequestMessage private readonly CommandMessage _wrappedMessage; // constructors - public CommandRequestMessage(CommandMessage wrappedMessage, Func shouldBeSent) - : base(Ensure.IsNotNull(wrappedMessage, nameof(wrappedMessage)).RequestId, shouldBeSent) + public CommandRequestMessage(CommandMessage wrappedMessage) + : base(Ensure.IsNotNull(wrappedMessage, nameof(wrappedMessage)).RequestId) { _wrappedMessage = Ensure.IsNotNull(wrappedMessage, nameof(wrappedMessage)); } diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandRequestMessageBinaryEncoder.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandRequestMessageBinaryEncoder.cs index a73e4ddcf0d..563d2e26ca0 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandRequestMessageBinaryEncoder.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandRequestMessageBinaryEncoder.cs @@ -32,7 +32,7 @@ public CommandRequestMessageBinaryEncoder(CommandMessageBinaryEncoder wrappedEnc public CommandRequestMessage ReadMessage() { var wrappedMessage = (CommandMessage)_wrappedEncoder.ReadMessage(); - return new CommandRequestMessage(wrappedMessage, null); + return new CommandRequestMessage(wrappedMessage); } public void WriteMessage(CommandRequestMessage message) diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandRequestMessageJsonEncoder.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandRequestMessageJsonEncoder.cs index 3d720858926..4ff6ab02b5d 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandRequestMessageJsonEncoder.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandRequestMessageJsonEncoder.cs @@ -32,7 +32,7 @@ public CommandRequestMessageJsonEncoder(CommandMessageJsonEncoder wrappedEncoder public CommandRequestMessage ReadMessage() { var wrappedMessage = (CommandMessage)_wrappedEncoder.ReadMessage(); - return new CommandRequestMessage(wrappedMessage, null); + return new CommandRequestMessage(wrappedMessage); } public void WriteMessage(CommandRequestMessage message) diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/QueryMessage.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/QueryMessage.cs index 0753233425e..feb0b0ffcf5 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/QueryMessage.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/QueryMessage.cs @@ -52,8 +52,7 @@ public QueryMessage( bool partialOk, bool noCursorTimeout, bool tailableCursor, - bool awaitData, - Func shouldBeSent = null) + bool awaitData) #pragma warning disable 618 : this( requestId, @@ -68,8 +67,7 @@ public QueryMessage( noCursorTimeout, oplogReplay: false, tailableCursor, - awaitData, - shouldBeSent) + awaitData) #pragma warning restore 618 { } @@ -88,9 +86,8 @@ public QueryMessage( bool noCursorTimeout, bool oplogReplay, // obsolete: OplogReplay is ignored by server versions 4.4.0 and newer bool tailableCursor, - bool awaitData, - Func shouldBeSent = null) - : base(requestId, shouldBeSent) + bool awaitData) + : base(requestId) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _query = Ensure.IsNotNull(query, nameof(query)); diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/RequestMessage.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/RequestMessage.cs index 95189ad8d7b..a1e0d7bbd70 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/RequestMessage.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/RequestMessage.cs @@ -13,7 +13,6 @@ * limitations under the License. */ -using System; using System.Threading; namespace MongoDB.Driver.Core.WireProtocol.Messages @@ -39,14 +38,12 @@ public static int GetNextRequestId() // fields private readonly int _requestId; - private readonly Func _shouldBeSent; private bool _wasSent; // constructors - protected RequestMessage(int requestId, Func shouldBeSent = null) + protected RequestMessage(int requestId) { _requestId = requestId; - _shouldBeSent = shouldBeSent; } // properties @@ -55,11 +52,6 @@ public int RequestId get { return _requestId; } } - public Func ShouldBeSent - { - get { return _shouldBeSent; } - } - public bool WasSent { get { return _wasSent; } diff --git a/src/MongoDB.Driver/Core/WireProtocol/WriteWireProtocolBase.cs b/src/MongoDB.Driver/Core/WireProtocol/WriteWireProtocolBase.cs deleted file mode 100644 index 83efcc179ec..00000000000 --- a/src/MongoDB.Driver/Core/WireProtocol/WriteWireProtocolBase.cs +++ /dev/null @@ -1,185 +0,0 @@ -/* Copyright 2010-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MongoDB.Bson; -using MongoDB.Bson.IO; -using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Driver.Core.Connections; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.WireProtocol.Messages; -using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; - -namespace MongoDB.Driver.Core.WireProtocol -{ - internal abstract class WriteWireProtocolBase : IWireProtocol - { - // fields - private readonly CollectionNamespace _collectionNamespace; - private readonly MessageEncoderSettings _messageEncoderSettings; - private readonly Func _shouldSendGetLastError; - private readonly WriteConcern _writeConcern; - - // constructors - protected WriteWireProtocolBase( - CollectionNamespace collectionNamespace, - MessageEncoderSettings messageEncoderSettings, - WriteConcern writeConcern, - Func shouldSendGetLastError = null) - { - _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); - _messageEncoderSettings = messageEncoderSettings; - _writeConcern = Ensure.IsNotNull(writeConcern, nameof(writeConcern)); - _shouldSendGetLastError = shouldSendGetLastError; - } - - // properties - protected CollectionNamespace CollectionNamespace - { - get { return _collectionNamespace; } - } - - protected WriteConcern WriteConcern - { - get { return _writeConcern; } - } - - // public properties - public bool MoreToCome => false; - - // methods - private BsonDocument CreateGetLastErrorCommand() - { - var command = _writeConcern.ToBsonDocument(); - command.InsertAt(0, new BsonElement("getLastError", 1)); - return command; - } - - private QueryMessage CreateGetLastErrorMessage(BsonDocument getLastErrorCommand) - { -#pragma warning disable 618 - return new QueryMessage( - RequestMessage.GetNextRequestId(), - _collectionNamespace.DatabaseNamespace.CommandCollection, - getLastErrorCommand, - null, - NoOpElementNameValidator.Instance, - 0, - -1, - true, - false, - false, - false, - false, - false, - _shouldSendGetLastError); -#pragma warning restore 618 - } - - private List CreateMessages(IConnection connection, out QueryMessage getLastErrorMessage) - { - var messages = new List(); - - var writeMessage = CreateWriteMessage(connection); - messages.Add(writeMessage); - - getLastErrorMessage = null; - if (_writeConcern.IsAcknowledged) - { - var getLastErrorCommand = CreateGetLastErrorCommand(); - getLastErrorMessage = CreateGetLastErrorMessage(getLastErrorCommand); - messages.Add(getLastErrorMessage); - } - - return messages; - } - - protected abstract RequestMessage CreateWriteMessage(IConnection connection); - - public WriteConcernResult Execute(IConnection connection, CancellationToken cancellationToken) - { - QueryMessage getLastErrorMessage; - var messages = CreateMessages(connection, out getLastErrorMessage); - - connection.SendMessages(messages, _messageEncoderSettings, cancellationToken); - if (getLastErrorMessage != null && getLastErrorMessage.WasSent) - { - var encoderSelector = new ReplyMessageEncoderSelector(BsonDocumentSerializer.Instance); - var reply = connection.ReceiveMessage(getLastErrorMessage.RequestId, encoderSelector, _messageEncoderSettings, cancellationToken); - return ProcessReply(connection.ConnectionId, getLastErrorMessage.Query, (ReplyMessage)reply); - } - else - { - return null; - } - } - - public async Task ExecuteAsync(IConnection connection, CancellationToken cancellationToken) - { - QueryMessage getLastErrorMessage; - var messages = CreateMessages(connection, out getLastErrorMessage); - - await connection.SendMessagesAsync(messages, _messageEncoderSettings, cancellationToken).ConfigureAwait(false); - if (getLastErrorMessage != null && getLastErrorMessage.WasSent) - { - var encoderSelector = new ReplyMessageEncoderSelector(BsonDocumentSerializer.Instance); - var reply = await connection.ReceiveMessageAsync(getLastErrorMessage.RequestId, encoderSelector, _messageEncoderSettings, cancellationToken).ConfigureAwait(false); - return ProcessReply(connection.ConnectionId, getLastErrorMessage.Query, (ReplyMessage)reply); - } - else - { - return null; - } - } - - private WriteConcernResult ProcessReply(ConnectionId connectionId, BsonDocument getLastErrorCommand, ReplyMessage reply) - { - if (reply.NumberReturned == 0) - { - throw new MongoCommandException(connectionId, "GetLastError reply had no documents.", getLastErrorCommand); - } - if (reply.NumberReturned > 1) - { - throw new MongoCommandException(connectionId, "GetLastError reply had more than one document.", getLastErrorCommand); - } - if (reply.QueryFailure) - { - throw new MongoCommandException(connectionId, "GetLastError reply had QueryFailure flag set.", getLastErrorCommand, reply.QueryFailureDocument); - } - - var response = reply.Documents.Single(); - - var notPrimaryOrNodeIsRecoveringException = ExceptionMapper.MapNotPrimaryOrNodeIsRecovering(connectionId, getLastErrorCommand, response, "err"); - if (notPrimaryOrNodeIsRecoveringException != null) - { - throw notPrimaryOrNodeIsRecoveringException; - } - - var writeConcernResult = new WriteConcernResult(response); - - var mappedException = ExceptionMapper.Map(connectionId, writeConcernResult); - if (mappedException != null) - { - throw mappedException; - } - - return writeConcernResult; - } - } -} diff --git a/tests/MongoDB.Driver.TestHelpers/Core/MockConnection.cs b/tests/MongoDB.Driver.TestHelpers/Core/MockConnection.cs index 2e35c86d5ff..6ee9c5ad81a 100644 --- a/tests/MongoDB.Driver.TestHelpers/Core/MockConnection.cs +++ b/tests/MongoDB.Driver.TestHelpers/Core/MockConnection.cs @@ -1,4 +1,4 @@ -/* Copyright 2013-present MongoDB Inc. +/* Copyright 2010-present MongoDB Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -232,15 +232,15 @@ public async Task ReceiveMessageAsync(int responseTo, IMessageE return (ResponseMessage)await action.GetEffectiveMessageAsync().ConfigureAwait(false); } - public void SendMessages(IEnumerable messages, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) + public void SendMessage(RequestMessage message, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) { - _sentMessages.AddRange(messages); + _sentMessages.Add(message); } - public Task SendMessagesAsync(IEnumerable messages, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) + public Task SendMessageAsync(RequestMessage message, MessageEncoderSettings messageEncoderSettings, CancellationToken cancellationToken) { - _sentMessages.AddRange(messages); - return Task.FromResult(null); + _sentMessages.Add(message); + return Task.CompletedTask; } public void SetReadTimeout(TimeSpan timeout) diff --git a/tests/MongoDB.Driver.Tests/Core/Connections/BinaryConnectionTests.cs b/tests/MongoDB.Driver.Tests/Core/Connections/BinaryConnectionTests.cs index c183dfba024..f39bb0a97a5 100644 --- a/tests/MongoDB.Driver.Tests/Core/Connections/BinaryConnectionTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Connections/BinaryConnectionTests.cs @@ -477,7 +477,7 @@ public void ReceiveMessage_should_complete_when_reply_is_already_on_the_stream( using (var stream = new BlockingMemoryStream()) { var messageToReceive = MessageHelper.BuildReply(new BsonDocument(), BsonDocumentSerializer.Instance, responseTo: 10); - MessageHelper.WriteResponsesToStream(stream, new[] { messageToReceive }); + MessageHelper.WriteResponsesToStream(stream, messageToReceive); var encoderSelector = new ReplyMessageEncoderSelector(BsonDocumentSerializer.Instance); @@ -545,7 +545,7 @@ public void ReceiveMessage_should_complete_when_reply_is_not_already_on_the_stre receiveMessageTask.IsCompleted.Should().BeFalse(); var messageToReceive = MessageHelper.BuildReply(new BsonDocument(), BsonDocumentSerializer.Instance, responseTo: 10); - MessageHelper.WriteResponsesToStream(stream, new[] { messageToReceive }); + MessageHelper.WriteResponsesToStream(stream, messageToReceive); var received = receiveMessageTask.GetAwaiter().GetResult(); @@ -601,7 +601,7 @@ public void ReceiveMessage_should_handle_out_of_order_replies( var messageToReceive10 = MessageHelper.BuildReply(new BsonDocument("_id", 10), BsonDocumentSerializer.Instance, responseTo: 10); var messageToReceive11 = MessageHelper.BuildReply(new BsonDocument("_id", 11), BsonDocumentSerializer.Instance, responseTo: 11); - MessageHelper.WriteResponsesToStream(stream, new[] { messageToReceive11, messageToReceive10 }); // out of order + MessageHelper.WriteResponsesToStream(stream, messageToReceive11, messageToReceive10); // out of order var received10 = receivedTask10.GetAwaiter().GetResult(); var received11 = receivedTask11.GetAwaiter().GetResult(); @@ -829,76 +829,57 @@ public void ReceiveMessage_should_throw_MongoConnectionClosedException_when_conn [Theory] [ParameterAttributeData] - public void SendMessages_should_throw_an_ArgumentNullException_if_messages_is_null( + public async Task SendMessage_should_throw_an_ArgumentNullException_if_message_is_null( [Values(false, true)] bool async) { - Action act; - if (async) - { - act = () => _subject.SendMessagesAsync(null, _messageEncoderSettings, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => _subject.SendMessages(null, _messageEncoderSettings, CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => _subject.SendMessageAsync(null, _messageEncoderSettings, CancellationToken.None)) : + Record.Exception(() => _subject.SendMessage(null, _messageEncoderSettings, CancellationToken.None)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void SendMessages_should_throw_an_ObjectDisposedException_if_the_connection_is_disposed( + public async Task SendMessage_should_throw_an_ObjectDisposedException_if_the_connection_is_disposed( [Values(false, true)] bool async) { var message = MessageHelper.BuildQuery(); _subject.Dispose(); - Action act; - if (async) - { - act = () => _subject.SendMessagesAsync(new[] { message }, _messageEncoderSettings, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => _subject.SendMessages(new[] { message }, _messageEncoderSettings, CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => _subject.SendMessageAsync(message, _messageEncoderSettings, CancellationToken.None)) : + Record.Exception(() => _subject.SendMessage(message, _messageEncoderSettings, CancellationToken.None)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void SendMessages_should_throw_an_InvalidOperationException_if_the_connection_is_not_open( + public async Task SendMessage_should_throw_an_InvalidOperationException_if_the_connection_is_not_open( [Values(false, true)] bool async) { var message = MessageHelper.BuildQuery(); - Action act; - if (async) - { - act = () => _subject.SendMessagesAsync(new[] { message }, _messageEncoderSettings, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - act = () => _subject.SendMessages(new[] { message }, _messageEncoderSettings, CancellationToken.None); - } + var exception = async ? + await Record.ExceptionAsync(() => _subject.SendMessageAsync(message, _messageEncoderSettings, CancellationToken.None)) : + Record.Exception(() => _subject.SendMessage(message, _messageEncoderSettings, CancellationToken.None)); - act.ShouldThrow(); + exception.Should().BeOfType(); } [Theory] [ParameterAttributeData] - public void SendMessages_should_put_the_messages_on_the_stream_and_raise_the_correct_events( + public void SendMessage_should_put_the_message_on_the_stream_and_raise_the_correct_events( [Values(false, true)] bool async) { using (var stream = new MemoryStream()) { - var message1 = MessageHelper.BuildQuery(query: new BsonDocument("x", 1)); - var message2 = MessageHelper.BuildQuery(query: new BsonDocument("y", 2)); + var message = MessageHelper.BuildQuery(query: new BsonDocument("x", 1)); if (async) { @@ -907,7 +888,7 @@ public void SendMessages_should_put_the_messages_on_the_stream_and_raise_the_cor _subject.OpenAsync(CancellationToken.None).GetAwaiter().GetResult(); _capturedEvents.Clear(); - _subject.SendMessagesAsync(new[] { message1, message2 }, _messageEncoderSettings, CancellationToken.None).GetAwaiter().GetResult(); + _subject.SendMessageAsync(message, _messageEncoderSettings, CancellationToken.None).GetAwaiter().GetResult(); } else { @@ -916,16 +897,15 @@ public void SendMessages_should_put_the_messages_on_the_stream_and_raise_the_cor _subject.Open(CancellationToken.None); _capturedEvents.Clear(); - _subject.SendMessages(new[] { message1, message2 }, _messageEncoderSettings, CancellationToken.None); + _subject.SendMessage(message, _messageEncoderSettings, CancellationToken.None); } - var expectedRequests = MessageHelper.TranslateMessagesToBsonDocuments(new[] { message1, message2 }); + var expectedRequests = MessageHelper.TranslateMessagesToBsonDocuments(new[] { message }); var sentRequests = MessageHelper.TranslateMessagesToBsonDocuments(stream.ToArray()); sentRequests.Should().BeEquivalentTo(expectedRequests); _capturedEvents.Next().Should().BeOfType(); _capturedEvents.Next().Should().BeOfType(); - _capturedEvents.Next().Should().BeOfType(); _capturedEvents.Next().Should().BeOfType(); _capturedEvents.Any().Should().BeFalse(); } diff --git a/tests/MongoDB.Driver.Tests/Core/Connections/BinaryConnection_CommandEventTests.cs b/tests/MongoDB.Driver.Tests/Core/Connections/BinaryConnection_CommandEventTests.cs index f4ddfddc9c9..a01da9bb54a 100644 --- a/tests/MongoDB.Driver.Tests/Core/Connections/BinaryConnection_CommandEventTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Connections/BinaryConnection_CommandEventTests.cs @@ -1,4 +1,4 @@ -/* Copyright 2013-present MongoDB Inc. +/* Copyright 2010-present MongoDB Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; @@ -126,13 +125,13 @@ public void Should_process_a_command() var requestMessage = MessageHelper.BuildCommand( expectedCommand, requestId: 10); - SendMessages(requestMessage); + SendMessage(requestMessage); var replyMessage = MessageHelper.BuildReply( expectedReply, BsonDocumentSerializer.Instance, responseTo: requestMessage.RequestId); - ReceiveMessages(replyMessage); + ReceiveMessage(replyMessage); var commandStartedEvent = (CommandStartedEvent)_capturedEvents.Next(); var commandSucceededEvent = (CommandSucceededEvent)_capturedEvents.Next(); @@ -162,13 +161,13 @@ public void Should_process_a_redacted_command(string commandJson, bool shouldBeR var requestMessage = MessageHelper.BuildCommand( command, requestId: 10); - SendMessages(requestMessage); + SendMessage(requestMessage); var replyMessage = MessageHelper.BuildReply( reply, BsonDocumentSerializer.Instance, responseTo: requestMessage.RequestId); - ReceiveMessages(replyMessage); + ReceiveMessage(replyMessage); var commandStartedEvent = (CommandStartedEvent)_capturedEvents.Next(); var commandSucceededEvent = (CommandSucceededEvent)_capturedEvents.Next(); @@ -197,13 +196,13 @@ public void Should_process_a_failed_command() var requestMessage = MessageHelper.BuildCommand( expectedCommand, requestId: 10); - SendMessages(requestMessage); + SendMessage(requestMessage); var replyMessage = MessageHelper.BuildReply( expectedReply, BsonDocumentSerializer.Instance, responseTo: requestMessage.RequestId); - ReceiveMessages(replyMessage); + ReceiveMessage(replyMessage); var commandStartedEvent = (CommandStartedEvent)_capturedEvents.Next(); var commandFailedEvent = (CommandFailedEvent)_capturedEvents.Next(); @@ -233,13 +232,13 @@ public void Should_process_a_redacted_failed_command(string commandJson, bool sh var requestMessage = MessageHelper.BuildCommand( command, requestId: 10); - SendMessages(requestMessage); + SendMessage(requestMessage); var replyMessage = MessageHelper.BuildReply( reply, BsonDocumentSerializer.Instance, responseTo: requestMessage.RequestId); - ReceiveMessages(replyMessage); + ReceiveMessage(replyMessage); var commandStartedEvent = (CommandStartedEvent)_capturedEvents.Next(); var commandFailedEvent = (CommandFailedEvent)_capturedEvents.Next(); @@ -288,15 +287,14 @@ public void Should_process_a_query_without_modifiers() var requestMessage = MessageHelper.BuildQuery( (BsonDocument)expectedCommand["filter"], requestId: 10); - SendMessages(requestMessage); - + SendMessage(requestMessage); var replyMessage = MessageHelper.BuildReply( expectedReplyDocuments, BsonDocumentSerializer.Instance, responseTo: requestMessage.RequestId, cursorId: expectedReply["cursor"]["id"].ToInt64()); - ReceiveMessages(replyMessage); + ReceiveMessage(replyMessage); var commandStartedEvent = (CommandStartedEvent)_capturedEvents.Next(); var commandSucceededEvent = (CommandSucceededEvent)_capturedEvents.Next(); @@ -374,7 +372,7 @@ public void Should_process_a_query_with_modifiers() using (EventContext.BeginFind(expectedCommand["batchSize"].ToInt32(), expectedCommand["limit"].ToInt32())) { - SendMessages(requestMessage); + SendMessage(requestMessage); } var replyMessage = MessageHelper.BuildReply( @@ -382,7 +380,7 @@ public void Should_process_a_query_with_modifiers() BsonDocumentSerializer.Instance, responseTo: requestMessage.RequestId, cursorId: expectedReply["cursor"]["id"].ToInt64()); - ReceiveMessages(replyMessage); + ReceiveMessage(replyMessage); var commandStartedEvent = (CommandStartedEvent)_capturedEvents.Next(); var commandSucceededEvent = (CommandSucceededEvent)_capturedEvents.Next(); @@ -420,13 +418,13 @@ public void Should_process_a_query_with_the_explain_modifier() var requestMessage = MessageHelper.BuildQuery( query, requestId: 10); - SendMessages(requestMessage); + SendMessage(requestMessage); var replyMessage = MessageHelper.BuildReply( expectedReply, BsonDocumentSerializer.Instance, responseTo: requestMessage.RequestId); - ReceiveMessages(replyMessage); + ReceiveMessage(replyMessage); var commandStartedEvent = (CommandStartedEvent)_capturedEvents.Next(); var commandSucceededEvent = (CommandSucceededEvent)_capturedEvents.Next(); @@ -459,13 +457,12 @@ public void Should_process_a_failed_query() var requestMessage = MessageHelper.BuildQuery( (BsonDocument)expectedCommand["filter"], requestId: 10); - SendMessages(requestMessage); - + SendMessage(requestMessage); var replyMessage = MessageHelper.BuildQueryFailedReply( queryFailureDocument, requestMessage.RequestId); - ReceiveMessages(replyMessage); + ReceiveMessage(replyMessage); var commandStartedEvent = (CommandStartedEvent)_capturedEvents.Next(); var commandFailedEvent = (CommandFailedEvent)_capturedEvents.Next(); @@ -485,19 +482,16 @@ public void Should_process_a_failed_query() commandFailedEvent.RequestId.Should().Be(commandStartedEvent.RequestId); } - private void SendMessages(params RequestMessage[] messages) + private void SendMessage(RequestMessage message) { - _subject.SendMessagesAsync(messages, _messageEncoderSettings, CancellationToken.None).Wait(); + _subject.SendMessageAsync(message, _messageEncoderSettings, CancellationToken.None).Wait(); } - private void ReceiveMessages(params ReplyMessage[] messages) + private void ReceiveMessage(ReplyMessage message) { - MessageHelper.WriteResponsesToStream(_stream, messages); + MessageHelper.WriteResponsesToStream(_stream, message); var encoderSelector = new ReplyMessageEncoderSelector(BsonDocumentSerializer.Instance); - foreach (var message in messages) - { - _subject.ReceiveMessageAsync(message.ResponseTo, encoderSelector, _messageEncoderSettings, CancellationToken.None).Wait(); - } + _subject.ReceiveMessageAsync(message.ResponseTo, encoderSelector, _messageEncoderSettings, CancellationToken.None).Wait(); } } } diff --git a/tests/MongoDB.Driver.Tests/Core/Helpers/MessageHelper.cs b/tests/MongoDB.Driver.Tests/Core/Helpers/MessageHelper.cs index 6af2baa3d27..bd73201dceb 100644 --- a/tests/MongoDB.Driver.Tests/Core/Helpers/MessageHelper.cs +++ b/tests/MongoDB.Driver.Tests/Core/Helpers/MessageHelper.cs @@ -225,7 +225,7 @@ public static List TranslateMessagesToBsonDocuments(byte[] bytes) return TranslateMessagesToBsonDocuments(TranslateBytesToRequests(bytes)); } - public static void WriteResponsesToStream(BlockingMemoryStream stream, IEnumerable responses) + public static void WriteResponsesToStream(BlockingMemoryStream stream, params ResponseMessage[] responses) { lock (stream.Lock) { diff --git a/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/CommandRequestMessageTests.cs b/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/CommandRequestMessageTests.cs index 955072e1b18..436638ea9f6 100644 --- a/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/CommandRequestMessageTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/CommandRequestMessageTests.cs @@ -1,4 +1,4 @@ -/* Copyright 2018-present MongoDB Inc. +/* Copyright 2010-present MongoDB Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * limitations under the License. */ -using System; using System.IO; using FluentAssertions; using MongoDB.Bson; @@ -31,12 +30,10 @@ public void constructor_should_initialize_instance() { var sections = new[] { new Type0CommandMessageSection(new BsonDocument(), BsonDocumentSerializer.Instance) }; var wrappedMessage = new CommandMessage(1, 2, sections, false); - Func shouldBeSent = () => true; - var result = new CommandRequestMessage(wrappedMessage, shouldBeSent); + var result = new CommandRequestMessage(wrappedMessage); result.WrappedMessage.Should().BeSameAs(wrappedMessage); - result.ShouldBeSent.Should().BeSameAs(shouldBeSent); } [Fact] @@ -75,17 +72,14 @@ public void GetEncoder_should_return_expected_result() } // private methods - private CommandRequestMessage CreateSubject( - CommandMessage wrappedMessage = null, - Func shouldBeSent = null) + private CommandRequestMessage CreateSubject(CommandMessage wrappedMessage = null) { if (wrappedMessage == null) { var sections = new[] { new Type0CommandMessageSection(new BsonDocument(), BsonDocumentSerializer.Instance) }; wrappedMessage = new CommandMessage(1, 2, sections, false); } - shouldBeSent = shouldBeSent ?? (() => true); - return new CommandRequestMessage(wrappedMessage, shouldBeSent); + return new CommandRequestMessage(wrappedMessage); } } } diff --git a/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandRequestMessageBinaryEncoderTests.cs b/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandRequestMessageBinaryEncoderTests.cs index cc65f6d01c0..6eb255c4159 100644 --- a/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandRequestMessageBinaryEncoderTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandRequestMessageBinaryEncoderTests.cs @@ -66,7 +66,7 @@ public void WriteMessage_should_delegate_to_wrapped_encoder() var document = new BsonDocument("x", 1); var sections = new[] { new Type0CommandMessageSection(document, BsonDocumentSerializer.Instance) }; var wrappedMessage = new CommandMessage(1, 2, sections, false); - var message = new CommandRequestMessage(wrappedMessage, () => true); + var message = new CommandRequestMessage(wrappedMessage); var expectedBytes = CreateMessageBytes(wrappedMessage); subject.WriteMessage(message); diff --git a/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/QueryMessageBinaryEncoderTests.cs b/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/QueryMessageBinaryEncoderTests.cs index 5f9196472d1..fbcf6c1685c 100644 --- a/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/QueryMessageBinaryEncoderTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/QueryMessageBinaryEncoderTests.cs @@ -1,4 +1,4 @@ -/* Copyright 2013-present MongoDB Inc. +/* Copyright 2010-present MongoDB Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -211,7 +211,7 @@ public void WriteMessage_should_invoke_encoding_post_processor( var command = BsonDocument.Parse("{ command : \"x\", writeConcern : { w : 0 } }"); var query = wrapped ? new BsonDocument("$query", command) : command; #pragma warning disable 618 - var message = new QueryMessage(0, collectionNamespace, query, null, NoOpElementNameValidator.Instance, 0, 0, false, false, false, false, false, false, null) + var message = new QueryMessage(0, collectionNamespace, query, null, NoOpElementNameValidator.Instance, 0, 0, false, false, false, false, false, false) { PostWriteAction = encoder => encoder.ChangeWriteConcernFromW0ToW1(), ResponseHandling = CommandResponseHandling.Ignore diff --git a/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandRequestMessageJsonEncoderTests.cs b/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandRequestMessageJsonEncoderTests.cs index 573a40d30a2..e4394ceb4ee 100644 --- a/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandRequestMessageJsonEncoderTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandRequestMessageJsonEncoderTests.cs @@ -66,7 +66,7 @@ public void WriteMessage_should_delegate_to_wrapped_encoder() var document = new BsonDocument("x", 1); var sections = new[] { new Type0CommandMessageSection(document, BsonDocumentSerializer.Instance) }; var wrappedMessage = new CommandMessage(1, 2, sections, false); - var message = new CommandRequestMessage(wrappedMessage, () => true); + var message = new CommandRequestMessage(wrappedMessage); var expectedJson = CreateMessageJson(wrappedMessage); subject.WriteMessage(message);