diff --git a/.evergreen/aws_lambda b/.evergreen/aws_lambda new file mode 120000 index 0000000000..3366dcbced --- /dev/null +++ b/.evergreen/aws_lambda @@ -0,0 +1 @@ +../.mod/drivers-evergreen-tools/.evergreen/aws_lambda \ No newline at end of file diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 7db7bc4ea7..57d4625654 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -168,6 +168,25 @@ functions: EOT + "run CSOT tests": + - command: shell.exec + type: test + params: + shell: bash + working_dir: "src" + script: | + ${PREPARE_SHELL} + # Needed for generating temporary aws credentials. + if [ -n "${FLE}" ]; + then + export AWS_ACCESS_KEY_ID="${fle_aws_key}" + export AWS_SECRET_ACCESS_KEY="${fle_aws_secret}" + export AWS_DEFAULT_REGION="${fle_aws_region}" + fi + export CSOT_SPEC_TESTS=1 + TEST_CMD="bundle exec rspec spec/spec_tests/client_side_operations_timeout_spec.rb" \ + .evergreen/run-tests.sh + "export FLE credentials": - command: shell.exec type: test @@ -496,36 +515,6 @@ task_groups: tasks: - "test-serverless" - - name: serverless_next_task_group - setup_group_can_fail_task: true - setup_group_timeout_secs: 1800 # 30 minutes - setup_group: - - func: "fetch source" - - func: "create expansions" - - command: ec2.assume_role - params: - role_arn: ${aws_test_secrets_role} - - command: shell.exec - params: - shell: "bash" - script: | - ${PREPARE_SHELL} - bash ${DRIVERS_TOOLS}/.evergreen/serverless/setup-secrets.sh serverless_next - bash ${DRIVERS_TOOLS}/.evergreen/serverless/create-instance.sh - - command: expansions.update - params: - file: serverless-expansion.yml - teardown_task: - - command: shell.exec - params: - script: | - ${PREPARE_SHELL} - bash ${DRIVERS_TOOLS}/.evergreen/serverless/setup-secrets.sh serverless_next - bash ${DRIVERS_TOOLS}/.evergreen/serverless/delete-instance.sh - - func: "upload test results" - tasks: - - "test-serverless" - - name: testatlas_task_group setup_group_can_fail_task: true setup_group_timeout_secs: 1800 # 30 minutes @@ -778,6 +767,9 @@ tasks: - name: "test-kerberos" commands: - func: "run Kerberos unit tests" + - name: "test-csot" + commands: + - func: "run CSOT tests" - name: "test-fle" commands: - func: "export FLE credentials" @@ -893,6 +885,7 @@ tasks: AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} AWS_SESSION_TOKEN: ${AWS_SESSION_TOKEN} LAMBDA_STACK_NAME: "dbx-ruby-lambda" + CLUSTER_PREFIX: "dbx-ruby-lambda" RVM_RUBY: ruby-3.2 MONGODB_URI: ${MONGODB_URI} axes: @@ -915,6 +908,10 @@ axes: variables: MONGODB_VERSION: "latest" CRYPT_SHARED_VERSION: "latest" + - id: "8.0" + display_name: "8.0" + variables: + MONGODB_VERSION: "8.0" - id: "7.0" display_name: "7.0" variables: @@ -971,6 +968,10 @@ axes: display_name: Replica Set variables: TOPOLOGY: replica-set + - id: "replica-set-single-node" + display_name: Replica Set (Single Node) + variables: + TOPOLOGY: replica-set-single-node - id: "sharded-cluster" display_name: Sharded variables: @@ -1078,18 +1079,24 @@ axes: - id: debian11 display_name: "Debian 11" run_on: debian11-small + - id: ubuntu2404 + display_name: "Ubuntu 24.04" + run_on: ubuntu2404-small + - id: ubuntu2404-arm + display_name: "Ubuntu 24.04 ARM64" + run_on: ubuntu2404-arm64-small - id: ubuntu2204 display_name: "Ubuntu 22.04" run_on: ubuntu2204-small + - id: ubuntu2204-arm + display_name: "Ubuntu 22.04 ARM64" + run_on: ubuntu2204-arm64-small - id: ubuntu2004 display_name: "Ubuntu 20.04" run_on: ubuntu2004-small - - id: rhel8 - display_name: "RHEL 8" - run_on: rhel80-small - - id: rhel8-arm - display_name: "RHEL 8 ARM64" - run_on: rhel82-arm64-small + - id: ubuntu1804 + display_name: "Ubuntu 18.04" + run_on: ubuntu1804-small - id: docker-distro display_name: Docker Distro @@ -1196,7 +1203,7 @@ axes: values: - id: mmapv1 display_name: MMAPv1 - run_on: rhel80-small + run_on: ubuntu1804-small variables: MMAPV1: 'true' @@ -1302,7 +1309,7 @@ buildvariants: ruby: "ruby-3.2" mongodb-version: "7.0" topology: standalone - run_on: rhel80-large + os: ubuntu2204 display_name: DriverBench tasks: - name: "driver-bench" @@ -1311,9 +1318,9 @@ buildvariants: matrix_spec: auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"] ruby: "ruby-3.2" - mongodb-version: ["latest", "7.0", "6.0"] + mongodb-version: ["latest", "8.0", "7.0"] topology: ["standalone", "replica-set", "sharded-cluster"] - os: rhel8 + os: ubuntu2204 display_name: ${auth-and-ssl} ${ruby} db-${mongodb-version} ${topology} tasks: - name: "test-mlaunch" @@ -1321,31 +1328,29 @@ buildvariants: - matrix_name: "mongo-recent" matrix_spec: ruby: ["ruby-3.2", "ruby-3.1", "jruby-9.4"] - mongodb-version: ["latest", "7.0", "6.0"] + mongodb-version: ["latest", "8.0", "7.0"] topology: ["standalone", "replica-set", "sharded-cluster"] - os: ['rhel8'] - # There is no latest for ubuntu2204, so we can't test it here. - # os: ['rhel8', 'ubuntu2204'] + os: ubuntu2204 display_name: "${mongodb-version} ${os} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" - - matrix_name: "mongo-recent-arm" + - matrix_name: "mongo-8-arm" matrix_spec: ruby: "ruby-3.2" - mongodb-version: ["latest", "7.0", "6.0"] + mongodb-version: [ '8.0' ] topology: ["standalone", "replica-set", "sharded-cluster"] - os: 'rhel8-arm' + os: ubuntu2404-arm display_name: "${mongodb-version} ${os} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" - matrix_name: "mongo-5.x" matrix_spec: - ruby: ["ruby-3.2", "ruby-2.7", "jruby-9.4"] - mongodb-version: ['5.3', '5.0'] + ruby: ["ruby-3.2", "ruby-3.1", "jruby-9.4"] + mongodb-version: ['5.3'] topology: ["standalone", "replica-set", "sharded-cluster"] - os: rhel8 + os: ubuntu1804 display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" @@ -1355,7 +1360,7 @@ buildvariants: ruby: ["ruby-3.0", "ruby-2.7"] mongodb-version: ['4.4', '4.2', '4.0'] topology: ["standalone", "replica-set", "sharded-cluster"] - os: rhel8 + os: ubuntu1804 display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" @@ -1365,7 +1370,7 @@ buildvariants: ruby: "ruby-2.7" mongodb-version: ['3.6'] topology: ["standalone", "replica-set", "sharded-cluster"] - os: rhel8 + os: ubuntu1804 display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" @@ -1376,18 +1381,18 @@ buildvariants: mongodb-version: "7.0" topology: load-balanced single-mongos: single-mongos - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} single-lb ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" - - matrix_name: "mongo-5.0-api-version" + - matrix_name: "mongo-api-version" matrix_spec: ruby: "ruby-3.2" - mongodb-version: '5.0' + mongodb-version: '7.0' topology: standalone api-version-required: yes - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} api-version-required ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" @@ -1398,18 +1403,28 @@ buildvariants: mongodb-version: "7.0" topology: "sharded-cluster" single-mongos: single-mongos - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} single-mongos ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" + - matrix_name: CSOT + matrix_spec: + ruby: "ruby-3.2" + mongodb-version: "7.0" + topology: replica-set-single-node + os: ubuntu2204 + display_name: "CSOT - ${mongodb-version}" + tasks: + - name: test-csot + - matrix_name: "no-retry-reads" matrix_spec: retry-reads: no-retry-reads ruby: "ruby-3.2" mongodb-version: "7.0" topology: ["standalone", "replica-set", "sharded-cluster"] - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} ${retry-reads} ${ruby}" tasks: - name: "test-mlaunch" @@ -1420,7 +1435,7 @@ buildvariants: ruby: "ruby-3.2" mongodb-version: "7.0" topology: [replica-set, sharded-cluster] - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} ${retry-writes} ${ruby}" tasks: - name: "test-mlaunch" @@ -1431,7 +1446,7 @@ buildvariants: mongodb-version: ['3.6', '4.0'] topology: ["standalone", "replica-set", "sharded-cluster"] storage-engine: mmapv1 - os: rhel8 + os: ubuntu1804 display_name: "${mongodb-version} ${topology} mmapv1 ${ruby}" tasks: - name: "test-mlaunch" @@ -1441,8 +1456,8 @@ buildvariants: lint: on ruby: "ruby-3.2" mongodb-version: "7.0" - topology: '*' - os: rhel8 + topology: ["standalone", "replica-set", "sharded-cluster"] + os: ubuntu2204 display_name: "${mongodb-version} ${topology} ${lint} ${ruby}" tasks: - name: "test-mlaunch" @@ -1453,7 +1468,7 @@ buildvariants: ruby: "ruby-3.2" mongodb-version: "7.0" topology: ["standalone", "replica-set", "sharded-cluster"] - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} fork ${ruby}" tasks: - name: "test-mlaunch" @@ -1461,10 +1476,10 @@ buildvariants: - matrix_name: "solo" matrix_spec: solo: on - ruby: ["ruby-3.2", "ruby-3.1", "ruby-3.0", "ruby-2.7", "jruby-9.4", "jruby-9.3"] + ruby: ["ruby-3.2", "ruby-3.1"] mongodb-version: "7.0" topology: ["standalone", "replica-set", "sharded-cluster"] - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} solo ${ruby}" tasks: - name: "test-mlaunch" @@ -1472,10 +1487,10 @@ buildvariants: - matrix_name: "stress older" matrix_spec: stress: on - ruby: ["ruby-2.7"] + ruby: "ruby-2.7" mongodb-version: ['4.2', '4.0', '3.6'] topology: replica-set - os: rhel8 + os: ubuntu1804 display_name: "${mongodb-version} ${topology} stress ${ruby}" tasks: - name: "test-mlaunch" @@ -1484,9 +1499,9 @@ buildvariants: matrix_spec: stress: on ruby: "ruby-3.2" - mongodb-version: ["6.0", "5.3"] + mongodb-version: ["8.0", "7.0"] topology: replica-set - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} stress ${ruby}" tasks: - name: "test-mlaunch" @@ -1494,7 +1509,7 @@ buildvariants: - matrix_name: "x509-tests" matrix_spec: auth-and-ssl: "x509" - ruby: "ruby-3.2" + ruby: 'ruby-3.1' # needs the latest_5x_mdb because run-tests.sh uses `mongo` to configure # the server for certain auth mechanisms. Once run-tests.sh is made smart # enough to install mongosh, and then use either mongo or mongosh @@ -1502,7 +1517,7 @@ buildvariants: # the latest stable db version. mongodb-version: "5.3" topology: standalone - os: rhel8 + os: ubuntu1804 display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" @@ -1513,31 +1528,31 @@ buildvariants: ruby: jruby-9.4 mongodb-version: "7.0" topology: ["standalone", "replica-set", "sharded-cluster"] - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" - - matrix_name: "zlib" + - matrix_name: zlib-"ruby-3.2" matrix_spec: auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ] - ruby: ["ruby-3.2", "ruby-2.7", "jruby-9.4"] + ruby: "ruby-3.2" mongodb-version: "7.0" topology: "replica-set" compressor: 'zlib' - os: rhel8 + os: ubuntu2204 display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" - - matrix_name: "snappy" + - matrix_name: snappy-"ruby-3.2" matrix_spec: auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ] - ruby: ["ruby-3.2", "ruby-2.7", "jruby-9.4"] + ruby: "ruby-3.2" mongodb-version: "7.0" topology: "replica-set" compressor: 'snappy' - os: rhel8 + os: ubuntu2204 display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" @@ -1546,40 +1561,112 @@ buildvariants: # apparently a zstd-jni gem for JRuby that we could investigate here; if # this test is ever supported to support jruby, the `sample_mri_rubies` # reference should be replaced with `sample_rubies`. - - matrix_name: "zstd-auth" + - matrix_name: zstd-auth-"ruby-3.2" matrix_spec: auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ] - ruby: ["ruby-3.2", "ruby-2.7"] + ruby: "ruby-3.2" mongodb-version: "7.0" topology: "replica-set" compressor: 'zstd' - os: rhel8 + os: ubuntu2204 display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" - - matrix_name: "activesupport" + - matrix_name: activesupport-"ruby-3.2" matrix_spec: - ruby: ["ruby-3.2", "ruby-2.7", "jruby-9.4"] + ruby: "ruby-3.2" mongodb-version: "7.0" topology: replica-set as: as - os: rhel8 + os: ubuntu2204 display_name: "AS ${mongodb-version} ${topology} ${ruby}" tasks: - name: "test-mlaunch" - - matrix_name: "bson" + - matrix_name: bson-"ruby-3.2" matrix_spec: - ruby: ["ruby-3.2", "ruby-2.7", "jruby-9.4"] + ruby: "ruby-3.2" mongodb-version: "7.0" topology: replica-set bson: "*" - os: rhel8 + os: ubuntu2204 display_name: "bson-${bson} ${mongodb-version} ${topology} ${ruby}" tasks: - name: "test-mlaunch" + - matrix_name: zlib-"ruby-2.7" + matrix_spec: + auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ] + ruby: "ruby-2.7" + mongodb-version: "6.0" + topology: "replica-set" + compressor: 'zlib' + os: ubuntu2004 + display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" + tasks: + - name: "test-mlaunch" + - matrix_name: snappy-"ruby-2.7" + matrix_spec: + auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ] + ruby: "ruby-2.7" + mongodb-version: "6.0" + topology: "replica-set" + compressor: 'snappy' + os: ubuntu2004 + display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" + tasks: + - name: "test-mlaunch" + + # the zstd-ruby gem does not support JRuby (explicitly). However, there is + # apparently a zstd-jni gem for JRuby that we could investigate here; if + # this test is ever supported to support jruby, the `sample_mri_rubies` + # reference should be replaced with `sample_rubies`. + - matrix_name: zstd-auth-"ruby-2.7" + matrix_spec: + auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ] + ruby: "ruby-2.7" + mongodb-version: "6.0" + topology: "replica-set" + compressor: 'zstd' + os: ubuntu2004 + display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" + tasks: + - name: "test-mlaunch" + + - matrix_name: activesupport-"ruby-2.7" + matrix_spec: + ruby: "ruby-2.7" + mongodb-version: "6.0" + topology: replica-set + as: as + os: ubuntu2004 + display_name: "AS ${mongodb-version} ${topology} ${ruby}" + tasks: + - name: "test-mlaunch" + + - matrix_name: bson-"ruby-2.7" + matrix_spec: + ruby: "ruby-2.7" + mongodb-version: "6.0" + topology: replica-set + bson: "*" + os: ubuntu2004 + display_name: "bson-${bson} ${mongodb-version} ${topology} ${ruby}" + tasks: + - name: "test-mlaunch" + + - matrix_name: "fle above 4.4" + matrix_spec: + auth-and-ssl: "noauth-and-nossl" + ruby: ["ruby-3.2", "ruby-3.1"] + topology: [replica-set, sharded-cluster] + mongodb-version: [ '6.0', '7.0', '8.0' ] + os: ubuntu2204 + fle: helper + display_name: "FLE: ${mongodb-version} ${topology} ${ruby}" + tasks: + - name: "test-fle" # kerberos integration tests are broken (RUBY-3266) # - matrix_name: "kerberos-integration" # matrix_spec: @@ -1594,7 +1681,7 @@ buildvariants: ruby: "ruby-3.2" mongodb-version: "7.0" topology: standalone - os: rhel8 + os: ubuntu2204 auth-and-ssl: kerberos display_name: "Kerberos Tests" tasks: @@ -1605,20 +1692,8 @@ buildvariants: auth-and-ssl: "noauth-and-nossl" ruby: "ruby-3.2" topology: [replica-set, sharded-cluster] - mongodb-version: [ 'latest', '7.0' ] - os: rhel8 - fle: helper - display_name: "FLE: ${mongodb-version} ${topology} ${ruby}" - tasks: - - name: "test-fle" - - - matrix_name: "fle" - matrix_spec: - auth-and-ssl: "noauth-and-nossl" - ruby: ["ruby-3.2", "ruby-2.7"] - topology: [replica-set, sharded-cluster] - mongodb-version: [ '6.0', '4.4', '4.2', '4.0' ] - os: rhel8 + mongodb-version: [ 'latest' ] + os: ubuntu2204 fle: helper display_name: "FLE: ${mongodb-version} ${topology} ${ruby}" tasks: @@ -1637,7 +1712,7 @@ buildvariants: # (depending on server version and what's available), we can bump this to # the latest stable db version. mongodb-version: "5.3" - os: ubuntu2004 + os: ubuntu1804 display_name: "AWS ${auth-and-ssl} ${mongodb-version} ${ruby}" tasks: - name: "test-aws-auth" @@ -1646,10 +1721,10 @@ buildvariants: matrix_spec: ocsp-verifier: true # No JRuby due to https://github.com/jruby/jruby-openssl/issues/210 - ruby: ["ruby-3.2", "ruby-3.1", "ruby-3.0", "ruby-2.7"] + ruby: ["ruby-3.2", "ruby-3.1"] topology: standalone mongodb-version: "7.0" - os: rhel8 + os: ubuntu2204 display_name: "OCSP verifier: ${mongodb-version} ${ruby}" tasks: - name: test-mlaunch @@ -1659,10 +1734,10 @@ buildvariants: ocsp-algorithm: ecdsa ocsp-must-staple: on ocsp-delegate: on - ruby: ["ruby-3.2", "ruby-2.7"] + ruby: "ruby-3.2" topology: standalone mongodb-version: "7.0" - os: rhel8 + os: ubuntu2204 auth-and-ssl: noauth-and-ssl display_name: "OCSP integration - must staple: ${mongodb-version} ${ruby}" tasks: @@ -1672,10 +1747,10 @@ buildvariants: matrix_spec: ocsp-algorithm: rsa ocsp-status: unknown - ruby: ["ruby-3.2", "ruby-2.7"] + ruby: "ruby-3.2" topology: standalone mongodb-version: "7.0" - os: rhel8 + os: ubuntu2204 auth-and-ssl: noauth-and-ssl display_name: "OCSP integration - unknown: ${mongodb-version} ${ruby}" tasks: @@ -1688,10 +1763,10 @@ buildvariants: ocsp-delegate: '*' ocsp-connectivity: pass extra-uri-options: "none" - ruby: ["ruby-3.2", "ruby-2.7"] + ruby: "ruby-3.2" topology: standalone mongodb-version: "7.0" - os: rhel8 + os: ubuntu2204 display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}" tasks: - name: test-mlaunch @@ -1702,10 +1777,10 @@ buildvariants: ocsp-delegate: '*' ocsp-connectivity: pass extra-uri-options: "none" - ruby: ["ruby-3.2", "ruby-2.7"] + ruby: "ruby-3.2" topology: standalone mongodb-version: "7.0" - os: rhel8 + os: ubuntu2204 display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}" tasks: - name: test-mlaunch @@ -1716,10 +1791,10 @@ buildvariants: ocsp-delegate: '*' ocsp-connectivity: fail extra-uri-options: "none" - ruby: ["ruby-3.2", "ruby-2.7"] + ruby: "ruby-3.2" topology: standalone mongodb-version: "7.0" - os: rhel8 + os: ubuntu2204 display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}" tasks: - name: test-mlaunch @@ -1730,10 +1805,10 @@ buildvariants: ocsp-delegate: '*' ocsp-connectivity: pass extra-uri-options: "tlsInsecure=true" - ruby: ["ruby-3.2", "ruby-2.7"] + ruby: "ruby-3.2" topology: standalone mongodb-version: "7.0" - os: rhel8 + os: ubuntu2204 display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}" tasks: - name: test-mlaunch @@ -1744,10 +1819,10 @@ buildvariants: ocsp-delegate: '*' ocsp-connectivity: pass extra-uri-options: "tlsInsecure=true" - ruby: ["ruby-3.2", "ruby-2.7"] + ruby: "ruby-3.2" topology: standalone mongodb-version: "7.0" - os: rhel8 + os: ubuntu2204 display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}" tasks: - name: test-mlaunch @@ -1758,10 +1833,10 @@ buildvariants: ocsp-delegate: '*' ocsp-connectivity: pass extra-uri-options: "tlsInsecure=true" - ruby: ["ruby-3.2", "ruby-2.7"] + ruby: "ruby-3.2" topology: standalone mongodb-version: "7.0" - os: rhel8 + os: ubuntu2204 display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}" tasks: - name: test-mlaunch @@ -1772,10 +1847,10 @@ buildvariants: ocsp-delegate: '*' ocsp-connectivity: pass extra-uri-options: "tlsAllowInvalidCertificates=true" - ruby: ["ruby-3.2", "ruby-2.7"] + ruby: "ruby-3.2" topology: standalone mongodb-version: "7.0" - os: rhel8 + os: ubuntu2204 display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}" tasks: - name: test-mlaunch @@ -1786,10 +1861,10 @@ buildvariants: ocsp-delegate: '*' ocsp-connectivity: pass extra-uri-options: "tlsAllowInvalidCertificates=true" - ruby: ["ruby-3.2", "ruby-2.7"] + ruby: "ruby-3.2" topology: standalone mongodb-version: "7.0" - os: rhel8 + os: ubuntu2204 display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}" tasks: - name: test-mlaunch @@ -1800,10 +1875,10 @@ buildvariants: ocsp-delegate: '*' ocsp-connectivity: pass extra-uri-options: "tlsAllowInvalidCertificates=true" - ruby: ["ruby-3.2", "ruby-2.7"] + ruby: "ruby-3.2" topology: standalone mongodb-version: "7.0" - os: rhel8 + os: ubuntu2204 display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}" tasks: - name: test-mlaunch @@ -1823,22 +1898,23 @@ buildvariants: ruby: jruby-9.4 topology: standalone mongodb-version: "7.0" - os: rhel8 + os: ubuntu2204 display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${mongodb-version} ${ruby}" tasks: - name: test-mlaunch - - matrix_name: testgcpkms-variant - matrix_spec: - ruby: "ruby-3.2" - fle: helper - topology: standalone - os: rhel8 - mongodb-version: "7.0" - display_name: "GCP KMS" - tasks: - - name: testgcpkms_task_group - batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README + # https://jira.mongodb.org/browse/RUBY-3540 + #- matrix_name: testgcpkms-variant + # matrix_spec: + # ruby: "ruby-3.2" + # fle: helper + # topology: standalone + # os: ubuntu2204 + # mongodb-version: "7.0" + # display_name: "GCP KMS" + # tasks: + # - name: testgcpkms_task_group + # batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README - matrix_name: testazurekms-variant matrix_spec: @@ -1855,41 +1931,27 @@ buildvariants: - matrix_name: atlas-full matrix_spec: ruby: "ruby-3.2" - os: rhel8 + os: ubuntu2204 display_name: "Atlas (Full)" tasks: - name: testatlas_full_task_group - matrix_name: "atlas" matrix_spec: - ruby: ["ruby-3.2", "ruby-3.1", "ruby-3.0", "ruby-2.7", "jruby-9.4", "jruby-9.3"] - os: rhel8 + ruby: ["ruby-3.2", "ruby-3.1"] + os: ubuntu2204 display_name: "Atlas tests ${ruby}" tasks: - name: testatlas_task_group -# Commented out, pending RUBY-3414 -# - matrix_name: "serverless" -# matrix_spec: -# # https://jira.mongodb.org/browse/RUBY-3217 -# # ruby: ["ruby-3.2", "ruby-3.1", "ruby-3.0", "ruby-2.7", "jruby-9.4", "jruby-9.3"] -# ruby: ["ruby-3.2", "ruby-3.1", "ruby-3.0", "ruby-2.7"] -# fle: path -# os: rhel8 -# display_name: "Atlas serverless ${ruby}" -# tasks: -# - name: serverless_task_group -# -# - matrix_name: "serverless-next" -# matrix_spec: -# # https://jira.mongodb.org/browse/RUBY-3217 -# # ruby: ["ruby-3.2", "ruby-3.1", "ruby-3.0", "ruby-2.7", "jruby-9.4", "jruby-9.3"] -# ruby: ["ruby-3.2", "ruby-3.1", "ruby-3.0", "ruby-2.7"] -# fle: path -# os: rhel8 -# display_name: "Atlas serverless-next ${ruby}" -# tasks: -# - name: serverless_next_task_group + - matrix_name: "serverless" + matrix_spec: + ruby: "ruby-3.2" + fle: path + os: ubuntu2204 + display_name: "Atlas serverless ${ruby}" + tasks: + - name: serverless_task_group - matrix_name: "aws-lambda" matrix_spec: diff --git a/.evergreen/config/axes.yml.erb b/.evergreen/config/axes.yml.erb index c433cf9c22..e28ab2773f 100644 --- a/.evergreen/config/axes.yml.erb +++ b/.evergreen/config/axes.yml.erb @@ -18,6 +18,10 @@ axes: variables: MONGODB_VERSION: "latest" CRYPT_SHARED_VERSION: "latest" + - id: "8.0" + display_name: "8.0" + variables: + MONGODB_VERSION: "8.0" - id: "7.0" display_name: "7.0" variables: @@ -74,6 +78,10 @@ axes: display_name: Replica Set variables: TOPOLOGY: replica-set + - id: "replica-set-single-node" + display_name: Replica Set (Single Node) + variables: + TOPOLOGY: replica-set-single-node - id: "sharded-cluster" display_name: Sharded variables: @@ -181,18 +189,24 @@ axes: - id: debian11 display_name: "Debian 11" run_on: debian11-small + - id: ubuntu2404 + display_name: "Ubuntu 24.04" + run_on: ubuntu2404-small + - id: ubuntu2404-arm + display_name: "Ubuntu 24.04 ARM64" + run_on: ubuntu2404-arm64-small - id: ubuntu2204 display_name: "Ubuntu 22.04" run_on: ubuntu2204-small + - id: ubuntu2204-arm + display_name: "Ubuntu 22.04 ARM64" + run_on: ubuntu2204-arm64-small - id: ubuntu2004 display_name: "Ubuntu 20.04" run_on: ubuntu2004-small - - id: rhel8 - display_name: "RHEL 8" - run_on: rhel80-small - - id: rhel8-arm - display_name: "RHEL 8 ARM64" - run_on: rhel82-arm64-small + - id: ubuntu1804 + display_name: "Ubuntu 18.04" + run_on: ubuntu1804-small - id: docker-distro display_name: Docker Distro @@ -297,7 +311,7 @@ axes: values: - id: mmapv1 display_name: MMAPv1 - run_on: rhel80-small + run_on: ubuntu1804-small variables: MMAPV1: 'true' diff --git a/.evergreen/config/common.yml.erb b/.evergreen/config/common.yml.erb index cacba6b5aa..6ea7e58801 100644 --- a/.evergreen/config/common.yml.erb +++ b/.evergreen/config/common.yml.erb @@ -165,6 +165,25 @@ functions: EOT + "run CSOT tests": + - command: shell.exec + type: test + params: + shell: bash + working_dir: "src" + script: | + ${PREPARE_SHELL} + # Needed for generating temporary aws credentials. + if [ -n "${FLE}" ]; + then + export AWS_ACCESS_KEY_ID="${fle_aws_key}" + export AWS_SECRET_ACCESS_KEY="${fle_aws_secret}" + export AWS_DEFAULT_REGION="${fle_aws_region}" + fi + export CSOT_SPEC_TESTS=1 + TEST_CMD="bundle exec rspec spec/spec_tests/client_side_operations_timeout_spec.rb" \ + .evergreen/run-tests.sh + "export FLE credentials": - command: shell.exec type: test @@ -493,36 +512,6 @@ task_groups: tasks: - "test-serverless" - - name: serverless_next_task_group - setup_group_can_fail_task: true - setup_group_timeout_secs: 1800 # 30 minutes - setup_group: - - func: "fetch source" - - func: "create expansions" - - command: ec2.assume_role - params: - role_arn: ${aws_test_secrets_role} - - command: shell.exec - params: - shell: "bash" - script: | - ${PREPARE_SHELL} - bash ${DRIVERS_TOOLS}/.evergreen/serverless/setup-secrets.sh serverless_next - bash ${DRIVERS_TOOLS}/.evergreen/serverless/create-instance.sh - - command: expansions.update - params: - file: serverless-expansion.yml - teardown_task: - - command: shell.exec - params: - script: | - ${PREPARE_SHELL} - bash ${DRIVERS_TOOLS}/.evergreen/serverless/setup-secrets.sh serverless_next - bash ${DRIVERS_TOOLS}/.evergreen/serverless/delete-instance.sh - - func: "upload test results" - tasks: - - "test-serverless" - - name: testatlas_task_group setup_group_can_fail_task: true setup_group_timeout_secs: 1800 # 30 minutes @@ -775,6 +764,9 @@ tasks: - name: "test-kerberos" commands: - func: "run Kerberos unit tests" + - name: "test-csot" + commands: + - func: "run CSOT tests" - name: "test-fle" commands: - func: "export FLE credentials" @@ -890,5 +882,6 @@ tasks: AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} AWS_SESSION_TOKEN: ${AWS_SESSION_TOKEN} LAMBDA_STACK_NAME: "dbx-ruby-lambda" + CLUSTER_PREFIX: "dbx-ruby-lambda" RVM_RUBY: ruby-3.2 MONGODB_URI: ${MONGODB_URI} diff --git a/.evergreen/config/standard.yml.erb b/.evergreen/config/standard.yml.erb index 668e65a9b1..7b91d4fac7 100644 --- a/.evergreen/config/standard.yml.erb +++ b/.evergreen/config/standard.yml.erb @@ -20,19 +20,26 @@ # all supported JRuby versions provided by 10gen/mongo-ruby-toolchain jrubies = %w( jruby-9.4 jruby-9.3 ) - supported_mri_rubies = %w( ruby-3.2 ruby-3.1 ruby-3.0 - ruby-2.7 ) + supported_mri_rubies_3 = %w( ruby-3.2 ruby-3.1 ruby-3.0 ) - supported_rubies = supported_mri_rubies + jrubies + supported_mri_rubies_3_ubuntu = %w( ruby-3.2 ruby-3.1 ) + + supported_mri_ruby_2 = "ruby-2.7".inspect + + supported_rubies = supported_mri_rubies_3 + + %w( ruby-2.7 ) + + jrubies # The latest stable version of MongoDB latest_stable_mdb = "7.0".inspect # so it gets quoted as a string # A few of the most recent MongoDB versions - actual_and_upcoming_mdb = %w( latest 7.0 6.0 ) + actual_and_upcoming_mdb = %w( latest 8.0 7.0 ) - recent_mdb = %w( 6.0 5.3 ) + recent_mdb = %w( 8.0 7.0 ) latest_5x_mdb = "5.3".inspect # so it gets quoted as a string + + all_dbs = %w(latest 8.0 7.0 6.0 5.3 5.0 4.4 4.2 4.0 3.6) %> buildvariants: @@ -41,7 +48,7 @@ buildvariants: ruby: <%= latest_ruby %> mongodb-version: <%= latest_stable_mdb %> topology: standalone - run_on: rhel80-large + os: ubuntu2204 display_name: DriverBench tasks: - name: "driver-bench" @@ -52,7 +59,7 @@ buildvariants: ruby: <%= latest_ruby %> mongodb-version: <%= actual_and_upcoming_mdb %> topology: <%= topologies %> - os: rhel8 + os: ubuntu2204 display_name: ${auth-and-ssl} ${ruby} db-${mongodb-version} ${topology} tasks: - name: "test-mlaunch" @@ -62,29 +69,27 @@ buildvariants: ruby: <%= recent_rubies %> mongodb-version: <%= actual_and_upcoming_mdb %> topology: <%= topologies %> - os: ['rhel8'] - # There is no latest for ubuntu2204, so we can't test it here. - # os: ['rhel8', 'ubuntu2204'] + os: ubuntu2204 display_name: "${mongodb-version} ${os} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" - - matrix_name: "mongo-recent-arm" + - matrix_name: "mongo-8-arm" matrix_spec: ruby: <%= latest_ruby %> - mongodb-version: <%= actual_and_upcoming_mdb %> + mongodb-version: [ '8.0' ] topology: <%= topologies %> - os: 'rhel8-arm' + os: ubuntu2404-arm display_name: "${mongodb-version} ${os} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" - matrix_name: "mongo-5.x" matrix_spec: - ruby: <%= sample_rubies %> - mongodb-version: ['5.3', '5.0'] + ruby: <%= recent_rubies %> + mongodb-version: ['5.3'] topology: <%= topologies %> - os: rhel8 + os: ubuntu1804 display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" @@ -94,17 +99,17 @@ buildvariants: ruby: <%= older_rubies %> mongodb-version: ['4.4', '4.2', '4.0'] topology: <%= topologies %> - os: rhel8 + os: ubuntu1804 display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" - matrix_name: "mongo-3.6" matrix_spec: - ruby: "ruby-2.7" + ruby: <%= supported_mri_ruby_2 %> mongodb-version: ['3.6'] topology: <%= topologies %> - os: rhel8 + os: ubuntu1804 display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" @@ -115,18 +120,18 @@ buildvariants: mongodb-version: <%= latest_stable_mdb %> topology: load-balanced single-mongos: single-mongos - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} single-lb ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" - - matrix_name: "mongo-5.0-api-version" + - matrix_name: "mongo-api-version" matrix_spec: ruby: <%= latest_ruby %> - mongodb-version: '5.0' + mongodb-version: '7.0' topology: standalone api-version-required: yes - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} api-version-required ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" @@ -137,18 +142,28 @@ buildvariants: mongodb-version: <%= latest_stable_mdb %> topology: "sharded-cluster" single-mongos: single-mongos - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} single-mongos ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" + - matrix_name: CSOT + matrix_spec: + ruby: <%= latest_ruby %> + mongodb-version: <%= latest_stable_mdb %> + topology: replica-set-single-node + os: ubuntu2204 + display_name: "CSOT - ${mongodb-version}" + tasks: + - name: test-csot + - matrix_name: "no-retry-reads" matrix_spec: retry-reads: no-retry-reads ruby: <%= latest_ruby %> mongodb-version: <%= latest_stable_mdb %> topology: <%= topologies %> - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} ${retry-reads} ${ruby}" tasks: - name: "test-mlaunch" @@ -159,18 +174,18 @@ buildvariants: ruby: <%= latest_ruby %> mongodb-version: <%= latest_stable_mdb %> topology: [replica-set, sharded-cluster] - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} ${retry-writes} ${ruby}" tasks: - name: "test-mlaunch" - matrix_name: mmapv1 matrix_spec: - ruby: "ruby-2.7" + ruby: <%= supported_mri_ruby_2 %> mongodb-version: ['3.6', '4.0'] topology: <%= topologies %> storage-engine: mmapv1 - os: rhel8 + os: ubuntu1804 display_name: "${mongodb-version} ${topology} mmapv1 ${ruby}" tasks: - name: "test-mlaunch" @@ -180,8 +195,8 @@ buildvariants: lint: on ruby: <%= latest_ruby %> mongodb-version: <%= latest_stable_mdb %> - topology: '*' - os: rhel8 + topology: <%= topologies %> + os: ubuntu2204 display_name: "${mongodb-version} ${topology} ${lint} ${ruby}" tasks: - name: "test-mlaunch" @@ -192,7 +207,7 @@ buildvariants: ruby: <%= latest_ruby %> mongodb-version: <%= latest_stable_mdb %> topology: <%= topologies %> - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} fork ${ruby}" tasks: - name: "test-mlaunch" @@ -200,10 +215,10 @@ buildvariants: - matrix_name: "solo" matrix_spec: solo: on - ruby: <%= supported_rubies %> + ruby: <%= supported_mri_rubies_3_ubuntu %> mongodb-version: <%= latest_stable_mdb %> topology: <%= topologies %> - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} solo ${ruby}" tasks: - name: "test-mlaunch" @@ -211,10 +226,10 @@ buildvariants: - matrix_name: "stress older" matrix_spec: stress: on - ruby: ["ruby-2.7"] + ruby: <%= supported_mri_ruby_2 %> mongodb-version: ['4.2', '4.0', '3.6'] topology: replica-set - os: rhel8 + os: ubuntu1804 display_name: "${mongodb-version} ${topology} stress ${ruby}" tasks: - name: "test-mlaunch" @@ -225,7 +240,7 @@ buildvariants: ruby: <%= latest_ruby %> mongodb-version: <%= recent_mdb %> topology: replica-set - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} stress ${ruby}" tasks: - name: "test-mlaunch" @@ -233,7 +248,7 @@ buildvariants: - matrix_name: "x509-tests" matrix_spec: auth-and-ssl: "x509" - ruby: <%= latest_ruby %> + ruby: 'ruby-3.1' # needs the latest_5x_mdb because run-tests.sh uses `mongo` to configure # the server for certain auth mechanisms. Once run-tests.sh is made smart # enough to install mongosh, and then use either mongo or mongosh @@ -241,7 +256,7 @@ buildvariants: # the latest stable db version. mongodb-version: <%= latest_5x_mdb %> topology: standalone - os: rhel8 + os: ubuntu1804 display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" @@ -252,31 +267,36 @@ buildvariants: ruby: <%= jrubies.first %> mongodb-version: <%= latest_stable_mdb %> topology: <%= topologies %> - os: rhel8 + os: ubuntu2204 display_name: "${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" - - matrix_name: "zlib" + <% [ + [latest_ruby, latest_stable_mdb, 'ubuntu2204'], + [supported_mri_ruby_2, '"6.0"', 'ubuntu2004'] + ].each do |rubies, mdb, distro| + %> + - matrix_name: <%= "zlib-#{rubies}" %> matrix_spec: auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ] - ruby: <%= sample_rubies %> - mongodb-version: <%= latest_stable_mdb %> + ruby: <%= rubies %> + mongodb-version: <%= mdb %> topology: "replica-set" compressor: 'zlib' - os: rhel8 + os: <%= distro %> display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" - - matrix_name: "snappy" + - matrix_name: <%= "snappy-#{rubies}" %> matrix_spec: auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ] - ruby: <%= sample_rubies %> - mongodb-version: <%= latest_stable_mdb %> + ruby: <%= rubies %> + mongodb-version: <%= mdb %> topology: "replica-set" compressor: 'snappy' - os: rhel8 + os: <%= distro %> display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" @@ -285,40 +305,52 @@ buildvariants: # apparently a zstd-jni gem for JRuby that we could investigate here; if # this test is ever supported to support jruby, the `sample_mri_rubies` # reference should be replaced with `sample_rubies`. - - matrix_name: "zstd-auth" + - matrix_name: <%= "zstd-auth-#{rubies}" %> matrix_spec: auth-and-ssl: [ "auth-and-ssl", "noauth-and-nossl" ] - ruby: <%= sample_mri_rubies %> - mongodb-version: <%= latest_stable_mdb %> + ruby: <%= rubies %> + mongodb-version: <%= mdb %> topology: "replica-set" compressor: 'zstd' - os: rhel8 + os: <%= distro %> display_name: "${compressor} ${mongodb-version} ${topology} ${auth-and-ssl} ${ruby}" tasks: - name: "test-mlaunch" - - matrix_name: "activesupport" + - matrix_name: <%= "activesupport-#{rubies}" %> matrix_spec: - ruby: <%= sample_rubies %> - mongodb-version: <%= latest_stable_mdb %> + ruby: <%= rubies %> + mongodb-version: <%= mdb %> topology: replica-set as: as - os: rhel8 + os: <%= distro %> display_name: "AS ${mongodb-version} ${topology} ${ruby}" tasks: - name: "test-mlaunch" - - matrix_name: "bson" + - matrix_name: <%= "bson-#{rubies}" %> matrix_spec: - ruby: <%= sample_rubies %> - mongodb-version: <%= latest_stable_mdb %> + ruby: <%= rubies %> + mongodb-version: <%= mdb %> topology: replica-set bson: "*" - os: rhel8 + os: <%= distro %> display_name: "bson-${bson} ${mongodb-version} ${topology} ${ruby}" tasks: - name: "test-mlaunch" + <% end %> + - matrix_name: "fle above 4.4" + matrix_spec: + auth-and-ssl: "noauth-and-nossl" + ruby: <%= supported_mri_rubies_3_ubuntu %> + topology: [replica-set, sharded-cluster] + mongodb-version: [ '6.0', '7.0', '8.0' ] + os: ubuntu2204 + fle: helper + display_name: "FLE: ${mongodb-version} ${topology} ${ruby}" + tasks: + - name: "test-fle" # kerberos integration tests are broken (RUBY-3266) # - matrix_name: "kerberos-integration" # matrix_spec: @@ -333,7 +365,7 @@ buildvariants: ruby: <%= latest_ruby %> mongodb-version: <%= latest_stable_mdb %> topology: standalone - os: rhel8 + os: ubuntu2204 auth-and-ssl: kerberos display_name: "Kerberos Tests" tasks: @@ -344,20 +376,8 @@ buildvariants: auth-and-ssl: "noauth-and-nossl" ruby: <%= latest_ruby %> topology: [replica-set, sharded-cluster] - mongodb-version: [ 'latest', '7.0' ] - os: rhel8 - fle: helper - display_name: "FLE: ${mongodb-version} ${topology} ${ruby}" - tasks: - - name: "test-fle" - - - matrix_name: "fle" - matrix_spec: - auth-and-ssl: "noauth-and-nossl" - ruby: <%= sample_mri_rubies %> - topology: [replica-set, sharded-cluster] - mongodb-version: [ '6.0', '4.4', '4.2', '4.0' ] - os: rhel8 + mongodb-version: [ 'latest' ] + os: ubuntu2204 fle: helper display_name: "FLE: ${mongodb-version} ${topology} ${ruby}" tasks: @@ -376,7 +396,7 @@ buildvariants: # (depending on server version and what's available), we can bump this to # the latest stable db version. mongodb-version: <%= latest_5x_mdb %> - os: ubuntu2004 + os: ubuntu1804 display_name: "AWS ${auth-and-ssl} ${mongodb-version} ${ruby}" tasks: - name: "test-aws-auth" @@ -385,10 +405,10 @@ buildvariants: matrix_spec: ocsp-verifier: true # No JRuby due to https://github.com/jruby/jruby-openssl/issues/210 - ruby: <%= supported_mri_rubies %> + ruby: <%= supported_mri_rubies_3_ubuntu %> topology: standalone mongodb-version: <%= latest_stable_mdb %> - os: rhel8 + os: ubuntu2204 display_name: "OCSP verifier: ${mongodb-version} ${ruby}" tasks: - name: test-mlaunch @@ -398,10 +418,10 @@ buildvariants: ocsp-algorithm: ecdsa ocsp-must-staple: on ocsp-delegate: on - ruby: <%= sample_mri_rubies %> + ruby: <%= latest_ruby %> topology: standalone mongodb-version: <%= latest_stable_mdb %> - os: rhel8 + os: ubuntu2204 auth-and-ssl: noauth-and-ssl display_name: "OCSP integration - must staple: ${mongodb-version} ${ruby}" tasks: @@ -411,10 +431,10 @@ buildvariants: matrix_spec: ocsp-algorithm: rsa ocsp-status: unknown - ruby: <%= sample_mri_rubies %> + ruby: <%= latest_ruby %> topology: standalone mongodb-version: <%= latest_stable_mdb %> - os: rhel8 + os: ubuntu2204 auth-and-ssl: noauth-and-ssl display_name: "OCSP integration - unknown: ${mongodb-version} ${ruby}" tasks: @@ -441,10 +461,10 @@ buildvariants: ocsp-delegate: '*' ocsp-connectivity: <%= outcome %> extra-uri-options: "<%= extra_uri_options %>" - ruby: <%= sample_mri_rubies %> + ruby: <%= latest_ruby %> topology: standalone mongodb-version: <%= latest_stable_mdb %> - os: rhel8 + os: ubuntu2204 display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${extra-uri-options} ${mongodb-version} ${ruby}" tasks: - name: test-mlaunch @@ -465,22 +485,23 @@ buildvariants: ruby: <%= jrubies.first %> topology: standalone mongodb-version: <%= latest_stable_mdb %> - os: rhel8 + os: ubuntu2204 display_name: "OCSP connectivity: ${ocsp-algorithm} ${ocsp-status} ${ocsp-delegate} ${mongodb-version} ${ruby}" tasks: - name: test-mlaunch - - matrix_name: testgcpkms-variant - matrix_spec: - ruby: <%= latest_ruby %> - fle: helper - topology: standalone - os: rhel8 - mongodb-version: <%= latest_stable_mdb %> - display_name: "GCP KMS" - tasks: - - name: testgcpkms_task_group - batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README + # https://jira.mongodb.org/browse/RUBY-3540 + #- matrix_name: testgcpkms-variant + # matrix_spec: + # ruby: <%= latest_ruby %> + # fle: helper + # topology: standalone + # os: ubuntu2204 + # mongodb-version: <%= latest_stable_mdb %> + # display_name: "GCP KMS" + # tasks: + # - name: testgcpkms_task_group + # batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README - matrix_name: testazurekms-variant matrix_spec: @@ -497,41 +518,27 @@ buildvariants: - matrix_name: atlas-full matrix_spec: ruby: <%= latest_ruby %> - os: rhel8 + os: ubuntu2204 display_name: "Atlas (Full)" tasks: - name: testatlas_full_task_group - matrix_name: "atlas" matrix_spec: - ruby: <%= supported_rubies %> - os: rhel8 + ruby: <%= supported_mri_rubies_3_ubuntu %> + os: ubuntu2204 display_name: "Atlas tests ${ruby}" tasks: - name: testatlas_task_group -# Commented out, pending RUBY-3414 -# - matrix_name: "serverless" -# matrix_spec: -# # https://jira.mongodb.org/browse/RUBY-3217 -# # ruby: <%= supported_rubies %> -# ruby: <%= supported_mri_rubies %> -# fle: path -# os: rhel8 -# display_name: "Atlas serverless ${ruby}" -# tasks: -# - name: serverless_task_group -# -# - matrix_name: "serverless-next" -# matrix_spec: -# # https://jira.mongodb.org/browse/RUBY-3217 -# # ruby: <%= supported_rubies %> -# ruby: <%= supported_mri_rubies %> -# fle: path -# os: rhel8 -# display_name: "Atlas serverless-next ${ruby}" -# tasks: -# - name: serverless_next_task_group + - matrix_name: "serverless" + matrix_spec: + ruby: <%= latest_ruby %> + fle: path + os: ubuntu2204 + display_name: "Atlas serverless ${ruby}" + tasks: + - name: serverless_task_group - matrix_name: "aws-lambda" matrix_spec: diff --git a/.evergreen/handle-paths.sh b/.evergreen/handle-paths.sh new file mode 120000 index 0000000000..77a67a0271 --- /dev/null +++ b/.evergreen/handle-paths.sh @@ -0,0 +1 @@ +../.mod/drivers-evergreen-tools/.evergreen/handle-paths.sh \ No newline at end of file diff --git a/.evergreen/run-deployed-lambda-aws-tests.sh b/.evergreen/run-deployed-lambda-aws-tests.sh deleted file mode 100755 index 3860563d18..0000000000 --- a/.evergreen/run-deployed-lambda-aws-tests.sh +++ /dev/null @@ -1,118 +0,0 @@ -#!/bin/bash -set -o errexit # Exit the script with error if any of the commands fail - -# Explanation of required environment variables: -# -# TEST_LAMBDA_DIRECTORY: The root of the project's Lambda sam project. -# DRIVERS_ATLAS_PUBLIC_API_KEY: The public Atlas key for the drivers org. -# DRIVERS_ATLAS_PRIVATE_API_KEY: The private Atlas key for the drivers org. -# DRIVERS_ATLAS_LAMBDA_USER: The user for the lambda cluster. -# DRIVERS_ATLAS_LAMBDA_PASSWORD: The password for the user. -# DRIVERS_ATLAS_GROUP_ID: The id of the individual projects under the drivers org, per language. -# LAMBDA_STACK_NAME: The name of the stack on lambda "dbx--lambda" -# AWS_REGION: The region for the function - generally us-east-1 - -VARLIST=( -TEST_LAMBDA_DIRECTORY -DRIVERS_ATLAS_PUBLIC_API_KEY -DRIVERS_ATLAS_PRIVATE_API_KEY -DRIVERS_ATLAS_LAMBDA_USER -DRIVERS_ATLAS_LAMBDA_PASSWORD -DRIVERS_ATLAS_GROUP_ID -LAMBDA_STACK_NAME -AWS_REGION -FUNCTION_NAME -) - -# Ensure that all variables required to run the test are set, otherwise throw -# an error. -for VARNAME in ${VARLIST[*]}; do -[[ -z "${!VARNAME}" ]] && echo "ERROR: $VARNAME not set" && exit 1; -done - -# Set up the common variables -. `dirname "$0"`/atlas/setup-variables.sh - -# Restarts the cluster's primary node. -restart_cluster_primary () -{ - echo "Testing Atlas primary restart..." - curl \ - --digest -u ${DRIVERS_ATLAS_PUBLIC_API_KEY}:${DRIVERS_ATLAS_PRIVATE_API_KEY} \ - -X POST \ - "${ATLAS_BASE_URL}/groups/${DRIVERS_ATLAS_GROUP_ID}/clusters/${FUNCTION_NAME}/restartPrimaries" -} - -# Deploys a lambda function to the set stack name. -deploy_lambda_function () -{ - echo "Deploying Lambda function..." - sam deploy \ - --stack-name "${FUNCTION_NAME}" \ - --capabilities CAPABILITY_IAM \ - --resolve-s3 \ - --parameter-overrides "MongoDbUri=${MONGODB_URI}" \ - --region ${AWS_REGION} -} - -# Get the ARN for the Lambda function we created and export it. -get_lambda_function_arn () -{ - echo "Getting Lambda function ARN..." - LAMBDA_FUNCTION_ARN=$(sam list stack-outputs \ - --stack-name ${FUNCTION_NAME} \ - --region ${AWS_REGION} \ - --output json | jq '.[] | select(.OutputKey == "MongoDBFunction") | .OutputValue' | tr -d '"' - ) - echo "Lambda function ARN: $LAMBDA_FUNCTION_ARN" - export LAMBDA_FUNCTION_ARN=$LAMBDA_FUNCTION_ARN -} - -delete_lambda_function () -{ - echo "Deleting Lambda Function..." - sam delete --stack-name ${FUNCTION_NAME} --no-prompts --region us-east-1 -} - -cleanup () -{ - delete_lambda_function -} - -trap cleanup EXIT SIGHUP - -cd "${TEST_LAMBDA_DIRECTORY}" - -sam build --use-container - -deploy_lambda_function - -get_lambda_function_arn - - -check_lambda_output () { - if grep -q FunctionError output.json - then - echo "Exiting due to FunctionError!" - exit 1 - fi - cat output.json | jq -r '.LogResult' | base64 --decode -} - -aws lambda invoke --function-name ${LAMBDA_FUNCTION_ARN} --log-type Tail lambda-invoke-standard.json > output.json -cat lambda-invoke-standard.json -check_lambda_output - -echo "Sleeping 1 minute to build up some streaming protocol heartbeats..." -sleep 60 -aws lambda invoke --function-name ${LAMBDA_FUNCTION_ARN} --log-type Tail lambda-invoke-frozen.json > output.json -cat lambda-invoke-frozen.json -check_lambda_output - -restart_cluster_primary - -echo "Sleeping 1 minute to build up some streaming protocol heartbeats..." -sleep 60 -aws lambda invoke --function-name ${LAMBDA_FUNCTION_ARN} --log-type Tail lambda-invoke-outage.json > output.json -cat lambda-invoke-outage.json -check_lambda_output diff --git a/.evergreen/run-tests-deployed-lambda.sh b/.evergreen/run-tests-deployed-lambda.sh index 2d587d12db..7e2a2f4832 100755 --- a/.evergreen/run-tests-deployed-lambda.sh +++ b/.evergreen/run-tests-deployed-lambda.sh @@ -14,4 +14,4 @@ export MONGODB_URI=${MONGODB_URI} export CLUSTER_PREFIX="ruby-driver-" export TEST_LAMBDA_DIRECTORY=`dirname "$0"`/../spec/faas/ruby-sam-app -. `dirname "$0"`/run-deployed-lambda-aws-tests.sh +. `dirname "$0"`/aws_lambda/run-deployed-lambda-aws-tests.sh diff --git a/.evergreen/run-tests-serverless.sh b/.evergreen/run-tests-serverless.sh index cd71e41d6e..e2447ba008 100755 --- a/.evergreen/run-tests-serverless.sh +++ b/.evergreen/run-tests-serverless.sh @@ -10,6 +10,8 @@ set_env_vars set_env_python set_env_ruby +source ${DRIVERS_TOOLS}/.evergreen/serverless/secrets-export.sh + bundle_install export MONGODB_URI=`echo ${SERVERLESS_URI} | sed -r 's/mongodb\+srv:\/\//mongodb\+srv:\/\/'"${SERVERLESS_ATLAS_USER}"':'"${SERVERLESS_ATLAS_PASSWORD}@"'/g'` @@ -23,7 +25,7 @@ else python3 -u .evergreen/mongodl.py --component crypt_shared -V ${SERVERLESS_MONGODB_VERSION} --out `pwd`/csfle_lib --target `host_distro` || true if test -f `pwd`/csfle_lib/lib/mongo_crypt_v1.so then - echo Usinn crypt shared library version ${SERVERLESS_MONGODB_VERSION} + echo Using crypt shared library version ${SERVERLESS_MONGODB_VERSION} export MONGO_RUBY_DRIVER_CRYPT_SHARED_LIB_PATH=`pwd`/csfle_lib/lib/mongo_crypt_v1.so else echo Failed to download crypt shared library @@ -31,8 +33,8 @@ else fi fi -if ! ( test -f /etc/os-release & grep -q ^ID.*rhel /etc/os-release & grep -q ^VERSION_ID.*8.0 /etc/os-release ); then - echo Serverless tests assume rhel80 +if ! ( test -f /etc/os-release & grep -q ^ID.*ubuntu /etc/os-release & grep -q ^VERSION_ID.*22.04 /etc/os-release ); then + echo Serverless tests assume ubuntu2204 echo If this has changed, update .evergreen/run-tests-serverless.sh as necessary exit -1 fi @@ -41,8 +43,8 @@ mkdir libmongocrypt cd libmongocrypt curl --retry 3 -fLo libmongocrypt-all.tar.gz "https://s3.amazonaws.com/mciuploads/libmongocrypt/all/master/latest/libmongocrypt-all.tar.gz" tar xf libmongocrypt-all.tar.gz -# We assume that serverless tests always use rhel80 -export LIBMONGOCRYPT_PATH=`pwd`/rhel-80-64-bit/nocrypto/lib64/libmongocrypt.so +# We assume that serverless tests always use ubuntu2204 +export LIBMONGOCRYPT_PATH=`pwd`/ubuntu2204-64/nocrypto/lib/libmongocrypt.so cd - cd .evergreen/csfle diff --git a/.evergreen/run-tests.sh b/.evergreen/run-tests.sh index a2203eb79a..8a73e15c3b 100755 --- a/.evergreen/run-tests.sh +++ b/.evergreen/run-tests.sh @@ -53,6 +53,10 @@ if [ "$FLE" = "helper" ]; then install_cmake fi +if test "$TOPOLOGY" = load-balanced; then + install_haproxy +fi + # Launching mongod under $MONGO_ORCHESTRATION_HOME # makes its log available through log collecting machinery @@ -65,6 +69,7 @@ fi calculate_server_args launch_ocsp_mock + launch_server "$dbdir" uri_options="$URI_OPTIONS" @@ -90,6 +95,9 @@ elif test "$TOPOLOGY" = replica-set; then # or it can try to send the commands to secondaries. hosts=localhost:27017,localhost:27018 uri_options="$uri_options&replicaSet=test-rs" +elif test "$TOPOLOGY" = replica-set-single-node; then + hosts=localhost:27017 + uri_options="$uri_options&replicaSet=test-rs" else hosts=localhost:27017 fi @@ -283,7 +291,7 @@ fi set_fcv -if test "$TOPOLOGY" = replica-set && ! echo "$MONGODB_VERSION" |fgrep -q 2.6; then +if test "$TOPOLOGY" = replica-set || test "$TOPOLOGY" = replica-set-single-node; then ruby -Ilib -I.evergreen/lib -rbundler/setup -rserver_setup -e ServerSetup.new.setup_tags fi diff --git a/.gitmodules b/.gitmodules index 6d428f359d..e1bab0957a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = https://github.com/mongodb-labs/drivers-evergreen-tools [submodule "spec/shared"] path = spec/shared - url = git@github.com:mongodb-labs/mongo-ruby-spec-shared.git + url = git@github.com:mongodb-labs/mongo-ruby-spec-shared.git diff --git a/.mod/drivers-evergreen-tools b/.mod/drivers-evergreen-tools index 8c948a286f..9728d31172 160000 --- a/.mod/drivers-evergreen-tools +++ b/.mod/drivers-evergreen-tools @@ -1 +1 @@ -Subproject commit 8c948a286f1b82427b5a409b8c42475b599d4d6f +Subproject commit 9728d311722e098eed27456861e0701d16e3b019 diff --git a/README.md b/README.md index cb45eb0edb..198295922c 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,9 @@ API documentation for the most recent release can be found To build API documentation for the master branch, check out the repository locally and run `rake docs`. +High-level driver documentation including tutorials and the reference that were in the docs folder can now be found +at the docs-ruby repository, [here](https://github.com/mongodb/docs-ruby) + ## Support Commercial support for the driver is available through the diff --git a/Rakefile b/Rakefile index 1c9c2ff8cc..5b4bcc744f 100644 --- a/Rakefile +++ b/Rakefile @@ -21,12 +21,12 @@ CLASSIFIERS = [ [%r,^spec_tests,, :spec], ] -RUN_PRIORITY = %i( +RUN_PRIORITY = (ENV['RUN_PRIORITY'] || %( tx_examples unit unit_server integration sdam_integration cursor_reaping query_cache spec spec_sdam_integration -) +)).split.map(&:to_sym) RSpec::Core::RakeTask.new(:spec) do |t| #t.rspec_opts = "--profile 5" if ENV['CI'] diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index e35d8850c9..0000000000 --- a/docs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_build diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index d4bb2cbb9e..0000000000 --- a/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 338b3c2974..0000000000 --- a/docs/README.md +++ /dev/null @@ -1,17 +0,0 @@ -Ruby MongoDB Driver Documentation -================================= - -This subdirectory contains the high-level driver documentation, including -tutorials and the reference. - -Building the documentation for publishing is done via the -[docs-ruby repo](https://github.com/mongodb/docs-ruby). - -To build the documentation locally for review, install `sphinx` and -`sphinx-book-theme`, then execute `make html` in this directory: - - pip install sphinx sphinx-book-theme - make html - -Note that the documentation generated in this manner wouldn't have the -BSON documentation included, nor are intersphinx links currently handled. diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 1a27671f07..0000000000 --- a/docs/conf.py +++ /dev/null @@ -1,57 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- Project information ----------------------------------------------------- - -project = 'Ruby MongoDB Driver' -copyright = '2021, MongoDB' - - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'alabaster' - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -source_suffix = { - '.txt': 'restructuredtext', -} - -html_theme = 'sphinx_book_theme' diff --git a/docs/contribute.txt b/docs/contribute.txt deleted file mode 100644 index a541b15fad..0000000000 --- a/docs/contribute.txt +++ /dev/null @@ -1,81 +0,0 @@ -************************ -Contribute to the Driver -************************ - -.. default-domain:: mongodb - -Report Bugs and Request Ruby Driver-Specific Features -===================================================== - -To report a bug in the driver or request a feature specific to the Ruby driver: - -1. Visit `our issue tracker `_ and login - (or create an account if you do not have one already). -2. Navigate to the `RUBY project `_. -3. Click :guilabel:`Create Issue` and fill out all of the applicable form - fields. - -When creating an issue, please keep in mind that all information in JIRA -for the RUBY project, as well as the core server (the SERVER project), -is publicly visible. - -**PLEASE DO:** - -- Provide as much information as possible about the issue. -- Provide detailed steps for reproducing the issue. -- Provide any applicable code snippets, stack traces and log data. - Do not include any sensitive data or server logs. -- Specify version numbers of the driver and MongoDB server. - -**PLEASE DO NOT:** - -- Provide any sensitive data or server logs. -- Report potential security issues publicly (see 'Security Issues' below). - -.. note:: - - Bug reports in JIRA for the Ruby driver and the core server (the **SERVER**) - projects are public. - -If you identified a potential security vulnerability in the Ruby driver or -any other MongoDB product, please report it according to the instructions found -in the :manual:`Create a Vulnerability Report -`. - - -Request Product Features -======================== - -To request a feature which is not specific to the Ruby driver, or which -affects more than the driver alone (for example, a feature which requires -MongoDB server support), please submit your idea through the -`MongoDB Feedback Forum `_. - - -Contribute Code -=============== - -The MongoDB Ruby driver source is located -`at GitHub `_. - -The list of known issues in the driver is available -`in JIRA `_. - -We recommend creating a JIRA ticket before starting work on a bug fix or -an improvement to the driver, to obtain feedback from the Ruby driver team -as to the proposed changes. A JIRA ticket is not required to submit -a pull request but it is appreciated, especially for non-trivial changes. - -Pull requests should be made against the ``master`` branch and -include relevant tests, if applicable. The Ruby driver team will backport -the changes to the stable branches, if needed. - -A MongoDB deployment is required to run the tests. Setup procedures and -recommendations for various deployments, as well as how to configure the -driver's test suite for the deployments, are covered in the `spec -readme `__. - -The driver is tested on `Evergreen `_, -MongoDB's in-house continuous integration platform. After a pull request -is created, one of the Ruby driver team engineers will schedule an Evergreen -build. diff --git a/docs/getting-started.txt b/docs/getting-started.txt deleted file mode 100644 index b92298a54c..0000000000 --- a/docs/getting-started.txt +++ /dev/null @@ -1,17 +0,0 @@ -.. _getting-started: - -*************** -Getting Started -*************** - -.. default-domain:: mongodb - -This section describes how to install the driver, installation prerequisites -and compatibility considerations. - -.. toctree:: - :titlesonly: - - installation - reference/driver-compatibility - support diff --git a/docs/includes/unicode-checkmark.rst b/docs/includes/unicode-checkmark.rst deleted file mode 100644 index 16f4e94764..0000000000 --- a/docs/includes/unicode-checkmark.rst +++ /dev/null @@ -1 +0,0 @@ -.. |checkmark| unicode:: U+2713 diff --git a/docs/includes/unicode-nbsp.rst b/docs/includes/unicode-nbsp.rst deleted file mode 100644 index aa0e52ea7a..0000000000 --- a/docs/includes/unicode-nbsp.rst +++ /dev/null @@ -1,2 +0,0 @@ -.. |nbsp| unicode:: 0xA0 - :trim: diff --git a/docs/index.txt b/docs/index.txt deleted file mode 100644 index d8ab98a35a..0000000000 --- a/docs/index.txt +++ /dev/null @@ -1,67 +0,0 @@ -.. http://www.mongodb.org/display/DOCS/Ruby+Language+Center - -.. _ruby-language-center: - -******************* -Ruby MongoDB Driver -******************* - -.. default-domain:: mongodb - -Welcome to the documentation site for the official MongoDB Ruby driver. -You can add the driver to your application to work with MongoDB in -Ruby. - -Get Started -=========== - -To get started with the Ruby driver, see :doc:`/installation` and -:doc:`/tutorials/quick-start`. Continue to :doc:`/tutorials` -for high level documentation for common operations. - -BSON -==== - -The Ruby BSON implementation is packaged in a separate gem with C and -Java extensions for speed depending on the runtime environment. - -For reference on the Ruby BSON gem, see the :doc:`/tutorials/bson`. - -Object Mappers -============== - -Because MongoDB is so easy to use, the basic Ruby driver can be the -best solution for many applications. But if you need validations, -associations, and other high-level data modeling functions, then you -may need Object Document Mapper. - -In the context of a Rails application, an Object Document Mapper -provides functionality equivalent to, but distinct from, ActiveRecord. -Because MongoDB is a document-based database, these mappers are called -Object Document Mappers (ODM) as opposed to Object Relational Mappers -(ORM). - -The ODM officially supported by MongoDB is Mongoid, originally written -by Durran Jordan. - -For tutorials on Mongoid, see the `Mongoid Manual `_. - -.. COMMENT For the actual build, see mongodb/docs-ruby repo which pulls the documentation source from: -.. mongo-ruby-driver, -.. bson-ruby, and -.. mongoid repos. - -.. class:: hidden - - .. toctree:: - :titlesonly: - - getting-started - tutorials - reference/connection-and-configuration - reference/working-with-data - reference/schema-operations - API - release-notes - reference/additional-resources - contribute diff --git a/docs/installation.txt b/docs/installation.txt deleted file mode 100644 index a5f404b044..0000000000 --- a/docs/installation.txt +++ /dev/null @@ -1,56 +0,0 @@ -************ -Installation -************ - -.. default-domain:: mongodb - -The Ruby driver is released as a gem hosted on `Rubygems -`_. - - -Prerequisites -============= - -Please see the :ref:`compatibility ` page for the list of -Ruby versions and MongoDB server versions that this release of the Ruby -driver is compatible with. - -The driver itself is written entirely in Ruby, however it depends on the -`bson library `_ which includes a C extension -for MRI and a compiled Java extension for JRuby. A working C compiler and Ruby -development headers and libraries are required when installing on MRI. -When installing on JRuby, JRE is sufficient because the ``bson`` gem includes -the compiled extension. - -Connecting to TLS-enabled MongoDB servers, using SCRAM authentication -(both SCRAM-SHA-1 and SCRAM-SHA-256) and using X.509 authentication (which -is performed over a TLS connection) requires the Ruby ``openssl`` extension -to be present and working. The :ref:`TLS compatibility ` -section provides further details on usage of newer TLS protocols like TLS 1.1. - - -.. _installation: - -Install the Gem -=============== - -Add ``mongo`` to your ``Gemfile``: - -.. code-block:: ruby - - gem "mongo", "~> 2" - -To install the driver manually: - -.. code-block:: sh - - gem install mongo -v '~> 2' - - -What's New -========== - -Please see the :ref:`release notes ` for the major changes -in each driver release and the `releases page on GitHub -`_ for the complete -list of changes for each release of the driver. diff --git a/docs/meta/404.txt b/docs/meta/404.txt deleted file mode 100644 index 4143ded8d6..0000000000 --- a/docs/meta/404.txt +++ /dev/null @@ -1,7 +0,0 @@ -:orphan: - -************** -File not found -************** - -The URL you requested does not exist or has been removed. diff --git a/docs/nesting-levels.txt b/docs/nesting-levels.txt deleted file mode 100644 index 6df05d1cbe..0000000000 --- a/docs/nesting-levels.txt +++ /dev/null @@ -1,17 +0,0 @@ -This file is not part of Ruby driver documentation proper, it is an internal -reference for the nesting levels that other files should be using. - -Ruby driver documentation nesting levels: - -********** -Page Title -********** - -First Level Heading -=================== - -Second Level Heading --------------------- - -Third Level Heading -``````````````````` diff --git a/docs/reference/additional-resources.txt b/docs/reference/additional-resources.txt deleted file mode 100644 index 218218f1c7..0000000000 --- a/docs/reference/additional-resources.txt +++ /dev/null @@ -1,200 +0,0 @@ -.. _ruby-external-resources: - -******************** -Additional Resources -******************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: twocols - -There are a number of good resources appearing all over the web for -learning about MongoDB and Ruby. A useful selection is listed below. If -you know of others, do let us know. - -Screencasts -=========== - -- `Introduction to MongoDB - Part I - `_ - - An introduction to MongoDB via the MongoDB shell. - -- `Introduction to MongoDB - Part II - `_ - - In this screencast, Joon You teaches how to use the Ruby driver to - build a simple Sinatra app. - -- `Introduction to MongoDB - Part III - `_ - - For the final screencast in the series, Joon You introduces - MongoMapper and Rails. - -- `RailsCasts: MongoDB & MongoMapper - `_ - - Ryan Bates' RailsCast introducing MongoDB and MongoMapper. - -- `RailsCasts: Mongoid `_ - - Ryan Bates' RailsCast introducing Mongoid. - -Presentations -============= - -- `Introduction to MongoDB (Video) `_ - - Mike Dirolf's introduction to MongoDB at Pivotal Labs, SF. - -- `MongoDB: A Ruby Document Store that doesn't rhyme with 'Ouch' - (Slides) - `_ - - Wynn Netherland's introduction to MongoDB with some comparisons to - CouchDB. - -- `MongoDB (is) for Rubyists (Slides) - `_ - - Kyle Banker's presentation on why MongoDB is for Rubyists (and all - human-oriented programmers). - -Articles -======== - -- `Why I Think Mongo is to Databases What Rails was to Frameworks - `_ - -- `What if a key-value store mated with a relational database system? - `_ - -- `Mongo Tips `_ - - John Nunemaker's articles on MongoDB and his Mongo Tips blog. - -- A series of articles on aggregation with MongoDB and Ruby: - - 1. `Part I: Introduction of Aggregation in MongoDB - `_ - - #. `Part II: MongoDB Grouping Elaborated - `_ - - #. `Part III: Introduction to Map-Reduce in MongoDB - `_ - -- `Does the MongoDB Driver Support Feature X? - `_ - - An explanation of how the MongoDB drivers usually automatically - support new database features. - -Projects -======== - -- `Capistrano Mongo Sync `_ - - Sync your local development db with your remote production db using capistrano. - -- `Simple Pub/Sub `_ - - A very simple pub/sub system. - -- `Mongo Queue `_ - - An extensible thread safe job/message queueing system that uses - MongoDB as the persistent storage engine. - -- `Resque-mongo `_ - - A port of the Github's Resque to MongoDB. - -- `Mongo Admin `_ - - A Rails plugin for browsing and managing MongoDB data. See the `live - demo `_. - -- `Sinatra Resource `_ - - Resource Oriented Architecture (REST) for Sinatra and MongoMapper. - -- `NewsMonger `_ - - A simple social news application demonstrating MongoMapper and Rails. - -- `Data Catalog API `_ - - From `Sunlight Labs `_, a non-trivial - application using MongoMapper and Sinatra. - -- `Watchtower `_ - - An example application using Mustache, MongoDB, and Sinatra. - -- `Shapado `_ - - A question and answer site similar to Stack Overflow. Live version at - `shapado.com `_. - -.. Does not seem to exist -.. - `Shorty `_ -.. A URL-shortener written with Sinatra and the MongoDB Ruby driver. - -Libraries -========= - -- `ActiveExpando `_ - - An extension to ActiveRecord to allow the storage of arbitrary - attributes in MongoDB. - -- `ActsAsTree (MongoMapper) - `_ - - ActsAsTree implementation for MongoMapper. - -- `Machinist adapter (MongoMapper) - `_ - - Machinist adapter using MongoMapper. - -- `Mongo-Delegate `_ - - A delegation library for experimenting with production data without - altering it. A quite useful pattern. - -- `Remarkable Matchers (MongoMapper) - `_ - - Testing / Matchers library using MongoMapper. - -- `OpenIdAuthentication, supporting MongoDB as the datastore - `_ - - Brandon Keepers' fork of OpenIdAuthentication supporting MongoDB. - -- `MongoTree (MongoRecord) - `_ - - MongoTree adds parent / child relationships to MongoRecord. - -- `Merb_MongoMapper - `_ - - A plugin for the Merb framework for supporting MongoMapper models. - -- `Mongolytics (MongoMapper) - `_ - - A web analytics tool. - -- `Rack-GridFS `_ - - A Rack middleware component that creates HTTP endpoints for files - stored in GridFS. diff --git a/docs/reference/aggregation.txt b/docs/reference/aggregation.txt deleted file mode 100644 index f9e63f9952..0000000000 --- a/docs/reference/aggregation.txt +++ /dev/null @@ -1,118 +0,0 @@ -.. _aggregation: - -*********** -Aggregation -*********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -:manual:`Aggregation framework` -operations process data records and return -computed results. Aggregation operations group values from -multiple documents together, and can perform a variety of -operations on the grouped data to return a single result. - -The Aggregation Pipeline -```````````````````````` - -The aggregation pipeline is a framework for data aggregation -modeled on the concept of data processing pipelines. Documents -enter a multi-stage pipeline that transforms the documents into -aggregated results. - -For a full explanation and a complete list of pipeline stages -and operators, see the -:manual:`manual`. - -The following example uses the aggregation pipeline on the -``restaurants`` sample dataset to find -a list of the total number of 5-star restaurants, grouped by restaurant -category. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test') - coll = client['restaurants'] - aggregation = coll.aggregate([ - { '$match'=> { 'stars'=> 5 } }, - { '$unwind'=> '$categories'}, - { '$group'=> { '_id'=> '$categories', 'fiveStars'=> { '$sum'=> 1 } } } - ]) - - aggregation.each do |doc| - #=> Yields a BSON::Document. - end - -Inside the ``aggregate`` method, the first pipeline stage filters out -all documents except those with ``5`` in the ``stars`` field. The -second stage unwinds the ``categories`` field, which is an array, and -treats each item in the array as a separate document. The third stage -groups the documents by category and adds up the number of matching -5-star results. - -Aggregation pipeline stages have a -:manual:`maximum memory use limit`. -To handle large datasets, set the ``allowDiskUse`` option to true to enable -writing data to temporary files. - -- You can call the ``allow_disk_use`` method the ``aggregation`` - object to get a new object with the option set: - -.. code-block:: ruby - - aggregation = coll.aggregate([ ]) - aggregation_with_disk_use = aggregation.allow_disk_use(true) - -- Or you can pass an option to the ``aggregate`` method: - -.. code-block:: ruby - - aggregation = coll.aggregate([ ], - :allow_disk_use => true) - -Single Purpose Aggregation Operations -````````````````````````````````````` - -MongoDB provides helper methods for some aggregation functions, -including :manual:`count` -and :manual:`distinct`. - -Count -~~~~~ - -The following example demonstrates how to use the ``count`` method to -find the total number of documents which have the exact array -``[ 'Chinese', 'Seafood' ]`` in the ``categories`` field. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test') - coll = client['restaurants'] - aggregation = coll.count({ 'categories': [ 'Chinese', 'Seafood' ] }) - - count = coll.count({ 'categories' => [ 'Chinese', 'Seafood' ] }) - -Distinct -~~~~~~~~ - -The ``distinct`` helper method eliminates results which contain -values and returns one record for each unique value. - -The following example returns a list of unique values for the -``categories`` field in the ``restaurants`` collection: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test') - coll = client['restaurants'] - aggregation = coll.distinct('categories') - - aggregation.each do |doc| - #=> Yields a BSON::Document. - end diff --git a/docs/reference/authentication.txt b/docs/reference/authentication.txt deleted file mode 100644 index 0c30c7056d..0000000000 --- a/docs/reference/authentication.txt +++ /dev/null @@ -1,534 +0,0 @@ -************** -Authentication -************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -MongoDB supports a variety of -:manual:`authentication mechanisms `. - -For more information about configuring your MongoDB server for each of -these authentication mechanisms see MongoDB's -:manual:`online documentation `. - -For more information about users and the Ruby driver's helpers for -user management, see the :ref:`User Management tutorial`. - - -Providing credentials -===================== - -If authentication is enabled, provide credentials when creating a new -client: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], - user: 'test', - password: '123', - database: 'mydb' ) - - # If using a URI: - client = Mongo::Client.new("mongodb://test:123@127.0.0.1:27017/mydb") - -Authentication credentials can be changed on a client instance to obtain -a new client using the ``Client#with`` method: - -.. code-block:: ruby - - authenticated_client = client.with( user: 'another-user', - password: '123' ) - -It is also possible to change the client's database and credentials in -one step: - -.. code-block:: ruby - - authenticated_music_client = client.with( database: 'music', - user:'test', - password:'123' ) - - -.. _auth-source: - -Auth Source -=========== - -A user's auth source is the database where that user's authentication -credentials are stored. - -The user's auth source may be specified whenever the credentials are specified: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], - database: 'mydb', - user: 'test', - password: '123', - auth_source: 'admin' ) - - # If using a URI: - client = Mongo::Client.new("mongodb://test:123@127.0.0.1:27017/mydb?authSource=admin") - -If no auth source is specified, then a default will be assumed by the client. -The default auth source depends on the authentication mechanism that is being -used to connect. - -For the ``MONGODB-CR``, ``SCRAM-SHA-1``, and ``SCRAM-SHA-256`` authentication -mechanisms, the default auth source is the database to which the client is -connecting; if no database is specified, ``admin`` database is the default -database and hence the default auth source. For the ``PLAIN`` mechanism (LDAP), -the default auth source is the database to which the client is connecting; -if no database is specified, the ``$external`` database is used as the -auth source. For the ``AWS``, ``GSSAPI`` and ``MONGODB_X509`` mechanisms, the -auth source is always ``$external``. - -When a client is constructed using an SRV URI, the driver will look for URI -options in a TXT DNS record that corresponds to the SRV record. Thus, for -example, MongoDB Atlas generally uses the ``admin`` database as its auth -source, but this is not specified in SRV URIs because the database is given -as a URI option on the TXT records. - -Note that when using SRV URIs, the SRV query and the TXT query are performed -separately. On systems where DNS resolution is not 100% reliable, the -failure to look up TXT records can cause authentication errors, as the driver -may end up using an incorrect auth source. If reliable DNS resolution cannot -be guaranteed, the auth source can be specified explicitly in SRV URIs as -a URI option: - -.. code-block:: ruby - - Mongo::Client.new("mongodb+srv://username:myRealPassword@cluster0.mongodb.net/test?w=majority&authSource=admin") - -.. note:: - - When changing the database using the ``with`` method, the auth source is - determined in the new ``Client`` instance using the full set of options - that applies to it. For example, if the original client had an auth source - specified, this auth source would take precedence over the database - given in the ``with`` call. If the original client did not have an auth - source specified, the new database would be the new auth source, subject - to the rules of the authentication mechanism used. - - -Authentication Mechanisms -========================= - -MongoDB supports several authentication mechanisms, as detailed in this section. -Authentication mechanism to use can be explicitly specified when a Client is -created; if authentication mechanism is not provided by the application, it is -selected as follows: - -- For MongoDB 4.0 and higher, the client performs SCRAM mechanism negotiation - with the server. If the user specified in client configuration permits - authentication with SCRAM-SHA-256, then SCRAM-SHA-256 is used for - authentication. Otherwise SCRAM-SHA-1 is used. -- For MongoDB 3.0 through 3.6, SCRAM-SHA-1 is used. -- For MongoDB 2.6, MONGODB-CR is used. - -Note that: - -- X.509, AWS, LDAP and Kerberos authentication mechanisms must always be - explicitly requested. -- If the MongoDB server that the client is connecting to supports SCRAM, - the client will attempt to authenticate using SCRAM if no authentication - mechanism is explicitly specified. To authenticate to MongoDB 3.0 and - higher servers using MONGODB-CR, the MONGODB-CR mechanism must be - explicitly requested. - -.. _scram: - -SCRAM -````` - -:manual:`SCRAM authentication ` is the default -authentication mechanism for MongoDB. There are two SCRAM mechanisms in -MongoDB: SCRAM-SHA-1 (available as of MongoDB 3.0) and SCRAM-SHA-256 -(available as of MongoDB 4.0). If an authentication mechanism is not -specified but user credentials are, the driver will attempt to use SCRAM -authentication on server 3.0 or newer and will negotiate the mechanism -to use based on the server version and the mechanisms defined for a -particular user (it is possible to configure a user in the server to only -allow SCRAM-SHA-1 mechanism, only SCRAM-SHA-256 mechanism or both). - -To explicitly specify SCRAM-SHA-1 as the authentication mechanism, use the -``auth_mech: :scram`` Ruby client option or the ``SCRAM-SHA-1`` as the value -for the ``authMechanism`` URI option, as follows: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], - database: 'mydb', - user: 'test', - password: '123', - auth_mech: :scram ) - - client = Mongo::Client.new("mongodb://test:123@127.0.0.1:27017/mydb?authMechanism=SCRAM-SHA-1") - -To explicitly specify SCRAM-SHA-256 as the authentication mechanism, use the -``auth_mech: :scram256`` Ruby client option or the ``SCRAM-SHA-256`` as the -value for the ``authMechanism`` URI option, as follows: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], - database: 'mydb', - user: 'test', - password: '123', - auth_mech: :scram256 ) - - client = Mongo::Client.new("mongodb://test:123@127.0.0.1:27017/mydb?authMechanism=SCRAM-SHA-256") - - -.. _x.509: - -Client Certificate (X.509) -`````````````````````````` - -The driver presents an X.509 certificate during TLS negotiation. -The MONGODB-X509 authentication mechanism authenticates a username -retrieved from the distinguished subject name of this certificate. - -.. note:: - - Since the username is retrieved from the certificate, a username does not - need to be specified. If a username is specified, it will be sent to the - server verbatim. If a password is provided, an error will be raised. - -This authentication method requires the use of TLS connections with -certificate validation. - -To authenticate the client, you will need a valid TLS certificate -and private encryption key. These can be stored in separate files, -or together in one file (in the PEM format). Even if the certificate -and private key are stored in the same file, you must specify the path to -that file by passing both the ``ssl_cert`` and ``ssl_key`` options -to the client. - -For more information about configuring X.509 authentication in MongoDB, -see the :manual:`X.509 tutorial in the MongoDB Manual -`. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], - auth_mech: :mongodb_x509, - ssl: true, - ssl_cert: '/path/to/client.pem', - ssl_key: '/path/to/client.pem', - ssl_ca_cert: '/path/to/ca.pem' ) - - -.. _aws-auth: - -AWS -``` - -*Requires MongoDB Enterprise Edition and server version 4.4 or later.* - -The AWS authentication mechanism uses AWS `Identity and Access Management (IAM) -`_ -and AWS `Security Token Service (STS) -`_ -to prove the client's identity to a MongoDB server. Briefly, AWS authentication -works as follows: - -1. The client uses AWS IAM credentials to create a signature that is sent to - the MongoDB server. -2. The server sends a request to AWS STS using the client's signature. -3. A successful STS request returns the username (technically, the ARN of - the IAM user or role) corresponding to the credentials that the client used. - The IAM user ARN is used by the server to look up a defined user, and the - client is considered to have authenticated as this user. - -.. note:: - - Unlike other authentication mechanisms, the username that the application - provides when creating a client and the username of the server user are - different: the username on the client is the AWS access key ID, but the - username on the server is the ARN of the IAM user or role corresponding - to the access key ID. - -AWS credentials are comprised of: - -- The access key ID. -- The secret access key. -- The optional session token. - -Authentication with `AWS IAM credentials -`_, -uses the access key ID and the secret access key. Authentication with -`temporary AWS IAM credentials -`_ -uses all three components. - -.. note:: - - The driver never sends the secret access key or the session token over - the network. - -Temporary credentials are used with: - -- STS `Assume Role `_ - requests. -- `EC2 instance roles `_. -- `ECS task roles `_. -- `AWS Lambda environment `_. -- `IAM roles for service accounts `_. - -The Ruby driver allows providing both regular and temporary credentials -explicitly as Ruby options or URI options. If credentials are not explicitly -provided, the driver will attempt to retrieve them from environment variables -described below and from EC2 instance and ECS task metadata endpoints. - -Providing Credentials Explicitly -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Regular (non-temporary) IAM credentials can be provided as Ruby options, -as follows: - -.. code-block:: ruby - - client = Mongo::Client.new(['mongodb.example.com'], - auth_mech: :aws, - user: '', - password: '', - database: 'mydb', - ) - -They can also be provided via a URI: - -.. code-block:: ruby - - client = Mongo::Client.new( - 'mongodb://:@mongodb.example.com/mydb?authMechanism=MONGODB-AWS') - -.. note:: - - When credentials are provided via a URI, they must be percent-escaped. - -To provide temporary credentials, specify the session token in the -authentication mechanism properties as follows: - -.. code-block:: ruby - - client = Mongo::Client.new(['mongodb.example.com'], - auth_mech: :aws, - user: '', - password: '', - auth_mech_properties: { - aws_session_token: '', - }, - database: 'mydb', - ) - -The temporary credentials can also be provided via a URI: - -.. code-block:: ruby - - client = Mongo::Client.new( - 'mongodb://:@mongodb.example.com/mydb?authMechanism=MONGODB-AWS&authMechanismProperties=AWS_SESSION_TOKEN:') - -.. _auto-retrieve-aws-credentials: - -Automatically Retrieving Credentials -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The client can retrieve credentials from the environment or from EC2 or ECS -metadata endpoints. To retrieve credentials automatically, specify the -AWS authentication mechanism but do not specify a username nor a password: - -.. code-block:: ruby - - client = Mongo::Client.new(['mongodb.example.com'], - auth_mech: :aws, - database: 'mydb', - ) - - # Using a URI: - client = Mongo::Client.new( - 'mongodb://mongodb.example.com/mydb?authMechanism=MONGODB-AWS') - -The driver will try to obtain credentials from the following sources, in -the specified order: - -- ``AWS_ACCESS_KEY_ID``, ``AWS_SECRET_ACCESS_KEY`` and ``AWS_SESSION_TOKEN`` - environment variables. These environment variables are recognized by - a variety of AWS-related libraries and tools such as the official - AWS Ruby SDK and the AWS CLI. They are also defined when running in an - AWS Lambda environment. -- The AWS STS `AssumeRoleWithWebIdentity action - `_. - This returns credentials associated with the service account token. This mechanism - requires the following environment variables to be set: - - - ``AWS_WEB_IDENTITY_TOKEN_FILE`` - path to a file containing the service - account token. - - ``AWS_ROLE_ARN`` - the Amazon Resource Name (ARN) of the role that the - caller is assuming. - - ``AWS_ROLE_SESSION_NAME`` (optional) - An identifier for the assumed role - session. If omitted, a random name will be generated by the driver. - -- The AWS `ECS task metadata endpoint - `_. - This returns credentials associated with the ECS task role assigned to - the container. -- The AWS `EC2 instance metadata endpoint - `_. - This returns credentials associated with the EC2 instance role assigned to - the instance. - -.. note:: - - A credentials source that provides any credentials must provide a complete - set of credentials. For example, the driver will raise an error if only - one of ``AWS_ACCESS_KEY_ID`` or ``AWS_SECRET_ACCESS_KEY`` environment - variables is populated but not the other. - -.. note:: - - If an application is running in an ECS container on an EC2 instance and - `the container is allowed access to the instance metadata - `_, - the driver will attempt to retrieve credentials for the AWS authentication - mechanism from the EC2 instance metadata endpoint, thus potentially - authenticating as the IAM role assigned to the EC2 instance, if it was not - able to retrieve ECS task role credentials from the ECS task endpoint. - - -.. _plain: - -LDAP (SASL PLAIN) -````````````````` - -*Requires MongoDB Enterprise Edition.* - -MongoDB Enterprise Edition supports the LDAP authentication mechanism -which allows you to delegate authentication using a Lightweight Directory -Access Protocol `LDAP `_ server. - -.. warning:: - - When using LDAP, passwords are sent to the server in plain text. For this - reason, we strongly recommend enabling TLS when using LDAP as your - authentication mechanism. - -For more information about configuring LDAP authentication in -MongoDB, see the :manual:`SASL/LDAP tutorial in the MongoDB Manual -`. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], - auth_mech: :plain, - ssl: true, - ssl_verify: true, - ssl_cert: '/path/to/client.pem', - ssl_ca_cert: '/path/to/ca.pem' ) - - -.. _kerberos: - -Kerberos (GSSAPI) -````````````````` - -*Requires MongoDB Enterprise Edition.* - -To configure the MongoDB server to use Kerberos, please refer to the -:manual:`server Kerberos documentation -`. - -To use the Kerberos authentication mechanism with the Ruby MongoDB driver, -an additional library implementing the Kerberos authenticator - -`mongo_kerberos `_ - must be -installed and loaded. To do so, add to your ``Gemfile``: - -.. code-block:: ruby - - gem 'mongo', '~> 2' - gem 'mongo_kerberos', '~> 2' - -... and add to your application code: - -.. code-block:: ruby - - require 'mongo' - require 'mongo_kerberos' - -If using Kerberos authentication with **MRI**, the password is not specified -in driver configuration and it is not sent to the MongoDB server by the driver. -Instead a Kerberos session must be established externally to the driver -and this session is used by the driver to prove the user's identity to -the server. Establishing this session requires that the host system is -configured for Kerberos authentication; refer to the `Kerberos documentation -`_ -or your operating system documentation for details. Use the `kinit utility -`_ -to establish a Kerberos session. - -If using Kerberos authentication with **JRuby**, the Kerberos session may -be estabished externally to the driver using the process described above -for MRI; alternatively, the password may be provided directly to the driver -via client configuration, or the path to a keytab file may be provided via -configuration stored in the ``java.security.auth.login.config`` system property. -Additionally, the Java runtime environment must be configured for Kerberos; -please refer to the `MongoDB Java Driver Kerberos documentation -`_ -for more information. - -.. note:: - - As per the server Kerberos documentation, the FQDN of the host - running MongoDB must be specified when using Kerberos authentication. - -.. note:: - - If using MongoDB URIs, be sure to percent-escape special characters like - ``/`` and ``@`` when they appear in the username. - -.. code-block:: ruby - - # Authenticate as appuser@MYREALM: - client = Mongo::Client.new("mongodb://appuser%40MYREALM@myserver.mycompany.com:27017/mydb?authMechanism=GSSAPI") - - # Authenticate as myapp/appuser@MYREALM: - client = Mongo::Client.new("mongodb://myapp%2Fappuser%40MYREALM@myserver.mycompany.com:27017/mydb?authMechanism=GSSAPI") - - # Authenticate using Ruby options: - client = Mongo::Client.new(['myserver.mycompany.com:27017'], - auth_mech: :gssapi, - user: 'myapp/appuser@MYREALM') - - -MONGODB-CR -`````````` - -*Deprecated:* MONGODB-CR mechanism is deprecated as of MongoDB 3.6 and -removed as of MongoDB 4.0. Please use `SCRAM authentication <#scram>`_ instead. - -MONGODB-CR was the default authentication mechanism for MongoDB through -version 2.6. - -The mechanism can be explicitly set with the credentials: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], - database: 'mydb', - user: 'test', - password: '123', - auth_mech: :mongodb_cr ) - -.. note:: - - If the MongoDB server that the client is connecting to supports SCRAM, - the client will attempt to authenticate using SCRAM if no authentication - mechanism is explicitly specified. To authenticate to MongoDB 3.0 and - higher servers using MONGODB-CR, the MONGODB-CR mechanism must be - explicitly requested. diff --git a/docs/reference/bulk-operations.txt b/docs/reference/bulk-operations.txt deleted file mode 100644 index 7f4d59b125..0000000000 --- a/docs/reference/bulk-operations.txt +++ /dev/null @@ -1,169 +0,0 @@ -*********** -Bulk Writes -*********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -.. _bulk-operations: - -The bulk write API sends several write operations to the server in a single -command. Use the bulk write API to reduce the number of network round-trips -when performing several writes at a time. For example, to efficiently perform -multiple updates, one might do: - -.. code-block:: ruby - - collection = client['colors'] - collection.bulk_write([ - { - update_one: { - filter: {name: 'yellow'}, - update: {'$set' => {hex: 'ffff00'}}, - }, - }, - { - update_one: { - filter: {name: 'purple'}, - update: {'$set' => {hex: '800080'}}, - }, - }, - ], ordered: true, write_concern: {w: :majority}) - -The following example shows how to execute different types of operations -in the same request: - -.. code-block:: ruby - - collection.bulk_write([ - { insert_one: { x: 1 } }, - { update_one: { - filter: { x: 1 }, - update: {'$set' => { x: 2 } }, - } }, - { replace_one: { - filter: { x: 2 }, - replacement: { x: 3 }, - } }, - ], :ordered => true) - -The first argument to ``bulk_write`` is the list of operations to perform. -Each operation must be specified as a hash with exactly one key which is -the operation name and the operation specification as the corresponding -value. The supported operations are detailed below. The ``bulk_write`` method -also accepts the following options: - -.. list-table:: - :header-rows: 1 - :widths: 40 80 - - * - Option - - Description - * - ``bypass_document_validation`` - - ``true`` or ``false``. Whether to bypass document validation. - * - ``ordered`` - - If the ``ordered`` option is set to ``true`` (which is the default), - the operations are applied in order and if any operation fails, subsequent - operations are not attempted. If the ``ordered`` option is set to ``false``, - all specified operations are attempted. - * - ``write_concern`` - - The write concern for the operation, specified as a hash. - -Valid bulk write operations are the following: - - -insert_one -========== - -.. code-block:: ruby - - { insert_one: { x: 1 } } - -.. note:: - - There is no ``insert_many`` bulk operation. To insert multiple documents, - specify multiple ``insert_one`` operations. - - -update_one -========== - -.. code-block:: ruby - - { update_one: { - filter: { x: 1 }, - update: { '$set' => { x: 2 } }, - # upsert is optional and defaults to false - upsert: true, - } } - - -update_many -=========== - -.. code-block:: ruby - - { update_many: { - filter: { x: 1 }, - update: { '$set' => { x: 2 } }, - # upsert is optional and defaults to false - :upsert => true, - } } - - -replace_one -=========== - -.. code-block:: ruby - - { replace_one: { - filter: { x: 1 }, - replacement: { x: 2 }, - # upsert is optional and defaults to false - upsert: true, - } } - -.. note:: - - The ``:replace_one`` operation requires that the replacement value is a - document. ``:replace_one`` does not recognize MongoDB update operators in - the replacement value. In a future release the driver is expected to - prohibit using keys beginning with ``$`` in the replacement document. - - -delete_one -========== - -.. code-block:: ruby - - { delete_one: { - filter: { x: 1 }, - } } - - -delete_many -=========== - -.. code-block:: ruby - - { delete_many: { - filter: { x: 1 }, - } } - - -Bulk Write Splitting -==================== - -The driver allows the application to submit arbitrarily large bulk write -requests. However, since MongoDB server limits the size of command documents -(currently this limit is 48 MiB), bulk writes that exceed this limit will be -split into multiple requests. - -When :ref:`client-side encryption ` is used, the -threshold used for bulk write splitting is reduced to allow for overhead in -the ciphertext. diff --git a/docs/reference/change-streams.txt b/docs/reference/change-streams.txt deleted file mode 100644 index e8f896a8c1..0000000000 --- a/docs/reference/change-streams.txt +++ /dev/null @@ -1,213 +0,0 @@ -.. _change-streams: - -************** -Change Streams -************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -As of version 3.6 of the MongoDB server, a new ``$changeStream`` pipeline stage -is supported in the aggregation framework. Specifying this stage first in an -aggregation pipeline allows users to request that notifications are sent for all -changes to a particular collection. As of MongoDB 4.0, change streams are -supported on databases and clusters in addition to collections. - -The Ruby driver provides an API for -receiving notifications for changes to a particular collection, database -or cluster using this -new pipeline stage. Although you can create a change stream using the pipeline -operator and aggregation framework directly, it is recommended to use the -driver API described below as the driver resumes the change stream one time -if there is a timeout, a network error, a server error indicating that a -failover is taking place or another type of a resumable error. - -Change streams on the server require a ``"majority"`` read concern or no -read concern. - -Change streams do not work properly with JRuby because of the issue documented here_. -Namely, JRuby eagerly evaluates ``#next`` on an Enumerator in a background -green thread, therefore calling ``#next`` on the change stream will cause -getMores to be called in a loop in the background. - -.. _here: https://github.com/jruby/jruby/issues/4212 - -Watching for Changes on a Collection -==================================== - -A collection change stream is created by calling the ``#watch`` method on a -collection: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test') - collection = client[:test] - stream = collection.watch - collection.insert_one(a: 1) - doc = stream.to_enum.next - process(doc) - - -You can also receive the notifications as they become available: - -.. code-block:: ruby - - stream = collection.watch - enum = stream.to_enum - while doc = enum.next - process(doc) - end - -The ``next`` method blocks and polls the cluster until a change is available. -Use the ``try_next`` method to iterate a change stream without blocking; this -method will wait up to max_await_time_ms milliseconds for changes from the server, -and if no changes are received it will return nil. If there is a non-resumable -error, both ``next`` and ``try_next`` will raise an exception. -See Resuming a Change Stream section below for an example that reads -changes from a collection indefinitely. - -The change stream can take filters in the aggregation framework pipeline -operator format: - -.. code-block:: ruby - - stream = collection.watch([{'$match' => { 'operationType' => {'$in' => ['insert', 'replace'] } } }, - {'$match' => { 'fullDocument.n' => { '$gte' => 1 } } } - ]) - enum = stream.to_enum - while doc = enum.next - process(doc) - end - -Watching for Changes on a Database -================================== - -A database change stream notifies on changes on any collection within the -database as well as database-wide events, such as the database being dropped. - -A database change stream is created by calling the ``#watch`` method on a -database object: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test') - database = client.database - stream = database.watch - client[:test].insert_one(a: 1) - doc = stream.to_enum.next - process(doc) - - -Watching for Changes on a Cluster -================================= - -A cluster change stream notifies on changes on any collection, any database -within the cluster as well as cluster-wide events. - -A cluster change stream is created by calling the ``#watch`` method on a -client object (not the cluster object): - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test') - stream = client.watch - client[:test].insert_one(a: 1) - doc = stream.to_enum.next - process(doc) - - -Closing a Change Stream -======================= - -You can close a change stream by calling its ``#close`` method: - -.. code-block:: ruby - - stream.close - - -Resuming a Change Stream -======================== - -A change stream consists of two types of operations: the initial aggregation -and ``getMore`` requests to receive the next batch of changes. - -The driver will automatically retry each ``getMore`` operation once on -network errors and when the server returns an error indicating it changed -state (for example, it is no longer the primary). The driver does not retry -the initial aggregation. - -In practical terms this means that, for example: - -- Calling ``collection.watch`` will fail if the cluster does not have - enough available nodes to satisfy the ``"majority"`` read preference. -- Once ``collection.watch`` successfully returns, if the cluster subsequently - experiences an election or loses a node, but heals quickly enough, - change stream reads via ``next`` or ``each`` methods will continue - transparently to the application. - -To indefinitely and reliably watch for changes without losing any changes or -processing a change more than once, the application must track the resume -token for the change stream and restart the change stream when it experiences -extended error conditions that cause the driver's automatic resume to also -fail. The following code snippet shows an example of iterating a change stream -indefinitely, retrieving the resume token using the ``resume_token`` change -stream method and restarting the change stream using the ``:resume_after`` -option on all MongoDB or network errors: - -.. code-block:: ruby - - token = nil - loop do - begin - stream = collection.watch([], resume_after: token) - enum = stream.to_enum - while doc = enum.next - process(doc) - token = stream.resume_token - end - rescue Mongo::Error - sleep 1 - end - end - -The above iteration is blocking at the ``enum.next`` call, and does not -permit resuming processing in the event the Ruby process running this code -is terminated. The driver also provides the ``try_next`` method which returns -``nil`` (after a small waiting period) instead of blocking indefinitely when -there are no changes in the change stream. Using the ``try_next`` method, -the resume token may be persisted after each ``getMore`` request, even when -a particular request does not return any changes, such that the resume token -remains at the top of the oplog and the application has an opportunity to -persist it should the process handling changes terminates: - -.. code-block:: ruby - - token = nil - loop do - begin - stream = collection.watch([], resume_after: token) - enum = stream.to_enum - doc = enum.try_next - if doc - process(doc) - end - token = stream.resume_token - # Persist +token+ to support resuming processing upon process restart - rescue Mongo::Error - sleep 1 - end - end - -Note that the resume token should be retrieved from the change stream after -every ``try_next`` call, even if the call returned no document. - -The resume token is also provided in the ``_id`` field of each change stream -document. Reading the ``_id`` field is not recommended because it may be -projected out by the application, and because using only the ``_id`` field -would not advance the resume token when a ``getMore`` returns no documents. diff --git a/docs/reference/collations.txt b/docs/reference/collations.txt deleted file mode 100644 index f58b63f0ce..0000000000 --- a/docs/reference/collations.txt +++ /dev/null @@ -1,311 +0,0 @@ -********** -Collations -********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Overview -======== - -.. versionadded:: 3.4 - -Collations are sets of rules for how to compare strings, typically in a -particular natural language. - -For example, in Canadian French, the last accent in a given word -determines the sorting order. - -Consider the following French words: - -.. code-block:: none - - cote < coté < côte < côté - -The sort order using the Canadian French collation would result in -the following: - -.. code-block:: none - - cote < côte < coté < côté - -If collation is unspecified, MongoDB uses the simple binary comparison for -strings. As such, the sort order of the words would be: - -.. code-block:: none - - cote < coté < côte < côté - -Usage -===== - -You can specify a default collation for collections and indexes when -they are created, or specify a collation for CRUD operations and -aggregations. For operations that support collation, MongoDB uses the -collection's default collation unless the operation specifies a -different collation. - -Collation Parameters -~~~~~~~~~~~~~~~~~~~~ - -.. code-block:: ruby - - 'collation' => { - 'locale' => , - 'caseLevel' => , - 'caseFirst' => , - 'strength' => , - 'numericOrdering' => , - 'alternate' => , - 'maxVariable' => , - 'normalization' => , - 'backwards' => - } - -The only required parameter is ``locale``, which the server parses as -an `ICU format locale ID `_. -For example, set ``locale`` to ``en_US`` to represent US English -or ``fr_CA`` to represent Canadian French. - -For a complete description of the available parameters, see the -:manual:`MongoDB manual entry`. - -.. _collation-on-collection: - -Assign a Default Collation to a Collection -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following example creates a new collection -called ``contacts`` on the ``test`` database and assigns a default -collation with the ``fr_CA`` locale. Specifying a collation when you -create the collection ensures that all operations involving a query -that are run against the -``contacts`` collection use the ``fr_CA`` collation, unless the query -specifies another collation. Any indexes on the new collection also -inherit the default collation, unless the creation command specifies -another collation. - -.. code-block:: ruby - - client = Mongo::Client.new([ "127.0.0.1:27017" ], :database => "test") - client[:contacts, { "collation" => { "locale" => "fr_CA" } } ].create - -.. _collation-on-index: - -Assign a Collation to an Index -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To specify a collation for an index, use the ``collation`` -option when you create the index. - -The following example creates an index on the ``name`` -field of the ``address_book`` collection, with the ``unique`` parameter -enabled and a default collation with ``locale`` set to ``en_US``. - -.. code-block:: ruby - - client = Mongo::Client.new([ "127.0.0.1:27017" ], :database => "test") - client[:address_book].indexes.create_one( { "first_name" => 1 }, - "unique" => true, - "collation" => { "locale" => "en_US" } - ) - -To use this index, make sure your queries also specify the same -collation. The following query uses the above index: - -.. code-block:: ruby - - client[:address_book].find({"first_name" : "Adam" }, - "collation" => { "locale" => "en_US" }) - -The following queries do **NOT** use the index. The first query uses no -collation, and the second uses a collation with a different ``strength`` -value than the collation on the index. - -.. code-block:: ruby - - client[:address_book].find({"first_name" : "Adam" }) - - client[:address_book].find({"first_name" : "Adam" }, - "collation" => { "locale" => "en_US", "strength" => 2 }) - -Operations that Support Collation -================================= - -All reading, updating, and deleting methods support collation. Some -examples are listed below. - -``find()`` and ``sort()`` -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Individual queries can specify a collation to use when matching -and sorting results. The following query and sort operation uses -a German collation with the ``locale`` parameter set to ``de``. - -.. code-block:: ruby - - client = Mongo::Client.new([ "127.0.0.1:27017" ], :database => "test") - docs = client[:contacts].find({ "city" => "New York" }, - { "collation" => { "locale" => "de" } }).sort( "name" => 1 ) - -``find_one_and_update()`` -~~~~~~~~~~~~~~~~~~~~~~~~~ - -A collection called ``names`` contains the following documents: - -.. code-block:: javascript - - { "_id" : 1, "first_name" : "Hans" } - { "_id" : 2, "first_name" : "Gunter" } - { "_id" : 3, "first_name" : "Günter" } - { "_id" : 4, "first_name" : "Jürgen" } - -The following ``find_one_and_update`` operation on the collection -does not specify a collation. - -.. code-block:: ruby - - client = Mongo::Client.new([ "127.0.0.1:27017" ], :database => "test") - doc = client[:names].find_one_and_update( {"first_name" => { "$lt" => "Gunter" }}, - { "$set" => { "verified" => true } }) - -Because ``Gunter`` is lexically first in the collection, -the above operation returns no results and updates no documents. - -Consider the same ``find_one_and_update`` operation but with the -collation specified. The locale is set to ``de@collation=phonebook``. - -.. note:: - - Some locales have a ``collation=phonebook`` option available for - use with languages which sort proper nouns differently from other - words. According to the ``de@collation=phonebook`` collation, - characters with umlauts come before the same characters without - umlauts. - -.. code-block:: ruby - - client = Mongo::Client.new([ "127.0.0.1:27017" ], :database => "test") - doc = client[:names].find_one_and_update( { "first_name" => { "$lt" => "Gunter" } }, - { "$set" => { "verified" => true } }, { "collation" => { "locale" => "de@collation=phonebook" }, - :return_document => :after } ) - -The operation returns the following updated document: - -.. code-block:: javascript - - { "_id" => 3, "first_name" => "Günter", "verified" => true } - -``find_one_and_delete()`` -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Set the ``numericOrdering`` collation parameter to ``true`` -to compare numeric string by their numeric values. - -The collection ``numbers`` contains the following documents: - -.. code-block:: javascript - - { "_id" : 1, "a" : "16" } - { "_id" : 2, "a" : "84" } - { "_id" : 3, "a" : "179" } - -The following example matches the first document in which field ``a`` -has a numeric value greater than 100 and deletes it. - -.. code-block:: ruby - - docs = numbers.find_one_and_delete({ "a" => { "$gt" => "100" } }, - { "collation" => { "locale" => "en", "numericOrdering" => true } }) - -After the above operation, the following documents remain in the -collection: - -.. code-block:: javascript - - { "_id" : 1, "a" : "16" } - { "_id" : 2, "a" : "84" } - -If you perform the same operation without collation, the server deletes -the first document it finds in which the lexical value of ``a`` is -greater than ``"100"``. - -.. code-block:: ruby - - numbers = client[:numbers] - docs = numbers.find_one_and_delete({ "a" => { "$gt" => "100" } }) - -After the above operation the document in which ``a`` was equal to -``"16"`` has been deleted, and the following documents remain in the -collection: - -.. code-block:: javascript - - { "_id" : 2, "a" : "84" } - { "_id" : 3, "a" : "179" } - -``delete_many()`` -~~~~~~~~~~~~~~~~~ - -You can use collations with all the various bulk operations which -exist in the Ruby driver. - -The collection ``recipes`` contains the following documents: - -.. code-block:: javascript - - { "_id" : 1, "dish" : "veggie empanadas", "cuisine" : "Spanish" } - { "_id" : 2, "dish" : "beef bourgignon", "cuisine" : "French" } - { "_id" : 3, "dish" : "chicken molé", "cuisine" : "Mexican" } - { "_id" : 4, "dish" : "chicken paillard", "cuisine" : "french" } - { "_id" : 5, "dish" : "pozole verde", "cuisine" : "Mexican" } - -Setting the ``strength`` parameter of the collation document to ``1`` -or ``2`` causes the server to disregard case in the query filter. The -following example uses a case-insensitive query filter -to delete all records in which the ``cuisine`` field matches -``French``. - -.. code-block:: ruby - - client = Mongo::Client.new([ "127.0.0.1:27017" ], :database => "test") - recipes = client[:recipes] - docs = recipes.delete_many({ "cuisine" => "French" }, - "collation" => { "locale" => "en_US", "strength" => 1 }) - -After the above operation runs, the documents with ``_id`` values of -``2`` and ``4`` are deleted from the collection. - -Aggregation -~~~~~~~~~~~ - -To use collation with an aggregation operation, specify a collation in -the aggregation options. - -The following aggregation example uses a collection called ``names`` -and groups the ``first_name`` field together, counts the total -number of results in each group, and sorts the -results by German phonebook order. - -.. code-block:: ruby - - aggregation = names.aggregate( - [ - { - "$group" => { "_id" => "$first_name", "name_count" => { "$sum" => 1 } } - }, - { - "$sort" => { "_id" => 1 } - }, - - ], { "collation" => { "locale" => "de@collation=phonebook" } } - ) - - aggregation.each do |doc| - #=> Yields a BSON::Document. - end diff --git a/docs/reference/collection-tasks.txt b/docs/reference/collection-tasks.txt deleted file mode 100644 index e5437c81c1..0000000000 --- a/docs/reference/collection-tasks.txt +++ /dev/null @@ -1,334 +0,0 @@ -*********** -Collections -*********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -MongoDB stores documents in collections. If a collection does not -exist, MongoDB creates the collection when you first insert a -document in that collection. - -You can also explicitly create a collection with various options, -such as setting the maximum size or the documentation validation rules. - -Time Series Collections -``````````````````````` - -Time series collections were added in MongoDB 5.0. You can read the documentation -`here `_. - -Time series collections efficiently store sequences of measurements over a -period of time. Time series data is any data that is collected over time and is -uniquely identified by one or more unchanging parameters. The unchanging -parameters that identify your time series data is generally your data source's -metadata. - -Creating a Time Series Collection ---------------------------------- -In order to create a time series collection, you must explicitly create a -collection using the time series options: - -.. code-block:: ruby - - opts = { - time_series: { - timeField: "timestamp", - metaField: "metadata", - granularity: "hours" - }, - expire_after: 604800 - } - - db['weather', opts].create - -When creating a time series collection, specify the following options: - -.. list-table:: - :header-rows: 1 - :widths: 40 80 - - * - Field - - Description - * - ``time_series[:timeField]`` - - Required. The name of the field which contains the date in each time series document. - * - ``time_series[:metaField]`` - - Optional. The name of the field which contains metadata in each time series document. The metadata in the specified field should be data that is used to label a unique series of documents. The metadata should rarely, if ever, change. - * - ``time_series[:granularity]`` - - Optional. Possible values are "seconds", "minutes", and "hours". By default, MongoDB sets the granularity to "seconds" for high-frequency ingestion. - * - ``:expireAfterSeconds`` - - Optional. Enable the automatic deletion of documents in a time series collection by specifying the number of seconds after which documents expire. MongoDB deletes expired documents automatically. - -See the MongoDB `docs `_ -for more information about time series collection options. - -Inserting into a Time Series Collection ---------------------------------------- - -Inserting into a time series collection is similar to inserting into a regular collection: - -.. code-block:: ruby - - db['weather'].insert_many([ - { - metadata: { sensorId: 5578, type: "temperature" }, - timestamp: Time.utc(2021, 5, 18, 0, 0, 0), - temp: 12 - }, - { - metadata: { sensorId: 5578, type: "temperature" }, - timestamp: Time.utc(2021, 5, 18, 4, 0, 0), - temp: 11 - }, - { - metadata: { sensorId: 5578, type: "temperature" }, - timestamp: Time.utc(2021, 5, 18, 8, 0, 0), - temp: 11 - }, - { - metadata: { sensorId: 5578, type: "temperature" }, - timestamp: Time.utc(2021, 5, 18, 12, 0, 0), - temp: 12 - }, - { - metadata: { sensorId: 5578, type: "temperature" }, - timestamp: Time.utc(2021, 5, 18, 16, 0, 0), - temp: 16 - }, - { - metadata: { sensorId: 5578, type: "temperature" }, - timestamp: Time.utc(2021, 5, 18, 20, 0, 0), - temp: 15 - }, { - metadata: { sensorId: 5578, type: "temperature" }, - timestamp: Time.utc(2021, 5, 19, 0, 0, 0), - temp: 13 - }, - { - metadata: { sensorId: 5578, type: "temperature" }, - timestamp: Time.utc(2021, 5, 19, 4, 0, 0), - temp: 12 - }, - { - metadata: { sensorId: 5578, type: "temperature" }, - timestamp: Time.utc(2021, 5, 19, 8, 0, 0), - temp: 11 - }, - { - metadata: { sensorId: 5578, type: "temperature" }, - timestamp: Time.utc(2021, 5, 19, 12, 0, 0), - temp: 12 - }, - { - metadata: { sensorId: 5578, type: "temperature" }, - timestamp: Time.utc(2021, 5, 19, 16, 0, 0), - temp: 17 - }, - { - metadata: { sensorId: 5578, type: "temperature" }, - timestamp: Time.utc(2021, 5, 19, 20, 0, 0), - temp: 12 - } - ]) - - -Querying a Time Series Collection ---------------------------------- - -Querying a time series collection is also very similar to a regular collection: - -.. code-block:: ruby - - weather.find(timestamp: Time.utc(2021, 5, 18, 0, 0, 0)).first - -The result of this query: - -.. code-block:: ruby - - { - "timestamp" => 2021-05-18 00:00:00 UTC, - "metadata" => { - "sensorId" => 5578, - "type" => "temperature" - }, - "temp" => 12, - "_id" => BSON::ObjectId('624dfb87d1327a60aeb048d2') - } - - -Using the Aggregation Pipeline on a Time Series Collection ----------------------------------------------------------- - -The aggregation pipeline can also be used for additional query functionality: - -.. code-block:: ruby - - weather.aggregate([ - { - "$project": { - date: { - "$dateToParts": { date: "$timestamp" } - }, - temp: 1 - } - }, - { - "$group": { - _id: { - date: { - year: "$date.year", - month: "$date.month", - day: "$date.day" - } - }, - avgTmp: { "$avg": "$temp" } - } - } - ]).to_a - -The example aggregation pipeline groups all documents by the date of the -measurement and then returns the average of all temperature measurements -that day: - -.. code-block:: ruby - - [{ - "_id" => { - "date" => { - "year" => 2021, - "month" => 5, - "day" => 18 - } - }, - "avgTmp" => 12.833333333333334 - }, - { - "_id" => { - "date" => { - "year" => 2021, - "month" => 5, - "day" => 19 - } - }, - "avgTmp" => 12.833333333333334 - }] - -See the MongoDB documentation on `time series collections `_ -for more information. - -Capped Collections -`````````````````` - -Capped collections have maximum size or document counts that prevent -them from growing beyond maximum thresholds. All capped collections must -specify a maximum size and may also specify a maximum document count. -MongoDB removes older documents if a collection reaches the maximum size -limit before it reaches the maximum document count. - -To create a :manual:`capped collection`, use -the ``capped: true`` option along with a ``size`` in bytes. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - collection = client[:artists, capped: true, size: 10000] - collection.create - collection.capped? # => true - -Convert an Existing Collection to Capped -```````````````````````````````````````` - -To convert an existing collection from non-capped to capped, use -the ``convertToCapped`` command. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - db = client.database - db.command({ 'convertToCapped' => 'artists', 'size' => 10000 }) - - -Document Validation -``````````````````` - -If you're using MongoDB version 3.2 or later, you can use -:manual:`document validation`. -Collections with validations compare each inserted or updated -document against the criteria specified in the validator option. -Depending on the ``validationLevel`` and ``validationAction``, MongoDB -either returns a warning, or refuses to insert or update the document -if it fails to meet the specified criteria. - -The following example creates a ``contacts`` collection with a validator -that specifies that inserted or updated documents should match at -least one of three following conditions: - -- the ``phone`` field is a string -- the ``email`` field matches the regular expression -- the ``status`` field is either ``Unknown`` or ``Incomplete``. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test') - client[:contacts, - - { - 'validator' => { '$or' => - [ - { 'phone' => { '$type' => "string" } }, - { 'email' => { '$regex' => /@mongodb\.com$/ } }, - { 'status' => { '$in' => [ "Unknown", "Incomplete" ] } } - ] - } - } - - ].create - -Add Validation to an Existing Collection -```````````````````````````````````````` - -To add document validation criteria to an existing collection, use the -``collMod`` command. The example below demonstrates how to add a -validation to the ``contacts`` collection, ensuring that all new -documents must contain an ``age`` field which is a number. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test') - db = client.database - db.command({ 'collMod' => 'contacts', - 'validator' => - { 'age' => - { '$type' => "number" } - } - }) - -Listing Collections -``````````````````` - -Use ``collections`` or ``collection_names`` methods on a database -objects to list collections: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - database = client.database - - database.collections # Returns an array of Collection objects. - database.collection_names # Returns an array of collection names as strings. - -Dropping Collections -```````````````````` - -To drop a collection, call ``drop`` on the collection object. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - artists = client[:artists] - artists.drop diff --git a/docs/reference/connection-and-configuration.txt b/docs/reference/connection-and-configuration.txt deleted file mode 100644 index 7532e73f98..0000000000 --- a/docs/reference/connection-and-configuration.txt +++ /dev/null @@ -1,18 +0,0 @@ -.. _connection-and-configuration: - -************************** -Connection & Configuration -************************** - -.. default-domain:: mongodb - -This section describes how to create the client objects and what configuration -options the driver provides, including authentication. - -.. toctree:: - :titlesonly: - - /reference/create-client - /reference/authentication - /reference/monitoring - /reference/user-management diff --git a/docs/reference/create-client.txt b/docs/reference/create-client.txt deleted file mode 100644 index 346aab93fd..0000000000 --- a/docs/reference/create-client.txt +++ /dev/null @@ -1,2142 +0,0 @@ -***************** -Creating a Client -***************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Using ``Mongo::Client`` -======================= - -To connect to a MongoDB deployment, create a ``Mongo::Client`` object. -Provide a list of hosts and options or a :manual:`connection string URI -` to the``Mongo::Client`` constructor. -The client's selected database defaults to ``admin``. - -By default, the driver will automatically detect the topology used by the -deployment and connect appropriately. - -To connect to a local standalone MongoDB deployment, specify the host and -port of the server. In most cases you would also specify the database name -to connect to; if no database name is specified, the client will use the -``admin`` database: - -.. code-block:: ruby - - Mongo::Client.new([ '127.0.0.1:27017' ], database: 'mydb') - - # Or using the URI syntax: - Mongo::Client.new("mongodb://127.0.0.1:27017/mydb") - -.. note:: - - The hostname ``localhost`` is treated specially by the driver and will - be resolved to IPv4 addresses only. - -To `connect to MongoDB Atlas `_, -specify the Atlas deployment URI: - -.. code-block:: ruby - - Mongo::Client.new("mongodb+srv://username:myRealPassword@cluster0.mongodb.net/mydb?w=majority") - -The driver will discover all nodes in the cluster and connect to them as -needed. - -Block Syntax ------------- - -Another way to create a Mongo::Client object is to use the block syntax: - -.. code-block:: ruby - - Mongo::Client.new(...) do |client| - # work with the client - end - -Note that when creating a client using this syntax, the client is automatically closed after the block finishes executing. - -Database Selection -================== - -By default, the client will connect to the ``admin`` database. - -The ``admin`` database is a special database in MongoDB often used for -administrative tasks and storing administrative data such as users and -roles (although users and roles may also be defined in other databases). -In a sharded cluster, the ``admin`` database -:manual:`exists on the config servers ` -rather than the shard servers. Although it is possible to use the ``admin`` -database for ordinary operations (such as storing application data), this -is not recommended and the application should explicitly specify the -database it wishes to use. - -The database can be specified during ``Client`` construction: - -.. code-block:: ruby - - # Using Ruby client options: - client = Mongo::Client.new(['localhost'], database: 'mydb') - - # Using a MongoDB URI: - client = Mongo::Client.new('mongodb://localhost/mydb') - -Given a ``Client`` instance, the ``use`` method can be invoked to obtain a -new ``Client`` instance configured with the specified database: - -.. code-block:: ruby - - client = Mongo::Client.new(['localhost'], database: 'mydb') - - admin_client = client.use('admin') - - # Issue an administrative command - admin_client.database.command(replSetGetConfig: 1).documents.first - -There are other special databases in MongoDB which should be only used for -their stated purposes: - -- The :manual:`config ` database. -- The :manual:`local ` database. -- The ``$external`` database, which is used with :ref:`PLAIN `, - :ref:`Kerberos ` and :ref:`X.509 ` authentication - mechanisms. - - -Connection Types -================ - -The driver will, by default, discover the type of deployment it is instructed -to connect to (except for load-balanced deployments) -and behave in the manner that matches the deployment type. -The subsections below describe how the driver behaves in each of the deployment -types as well as how to force particular behavior, bypassing automatic -deployment type detection. - -Note that the detection of deployment type happens when the driver receives -the first reply from any of the servers it is instructed to connect to -(unless the load-balancing mode is requested, see below). The driver will -remain in the discovered or configured topology even if the underlying -deployment is replaced by one of a different type. In particular, when -replacing a replica set with a sharded cluster at the same address -the client instance must be recreated (such as by restarting the application) -for it to communicate with the sharded cluster. - -Automatic discovery of load-balanced deployments is currently not supported. -Load-balanced deployments will be treated as deployments of their underlying -type, which would generally be sharded clusters. The driver will fail to -correctly operate when treating a load-balanced deployment as a sharded -cluster, therefore when the deployment is a load-balanced one the client -must be explicitly configured to :ref:`connect to a load balancer -`. - - -Standalone Server Connection ----------------------------- - -If the deployment is a single server, also known as a standalone deployment, -all operations will be directed to the specified server. - -If the server is shut down and replaced by a replica set node, the driver -will continue sending all operations to that node, even if the node is or -becomes a secondary. - -To force a standalone connection, see the :ref:`direct connection -` section below. - - -.. _connect-replica-set: - -Replica Set Connection ----------------------- - -When connecting to a :manual:`replica set`, it is sufficient -to pass the address of any node in the replica set to the driver. -The node does not have to be the primary and it may be a hidden node. -The driver will then automatically discover the remaining nodes. - -However, it is recommended to specify all nodes that are part of the -replica set, so that in the event of one or more nodes being unavailable -(for example, due to maintenance or reconfiguration) the driver can still -connect to the replica set. - -Replica set connection examples: - -.. code-block:: ruby - - Mongo::Client.new([ '127.0.0.1:27017' ], database: 'mydb') - - Mongo::Client.new([ '127.0.0.1:27017', '127.0.0.1:27018' ], database: 'mydb') - - # Or using the URI syntax: - Mongo::Client.new("mongodb://127.0.0.1:27017,127.0.0.1:27018/mydb") - -To make the driver verify the replica set name upon connection, pass it using -the ``replica_set`` Ruby option or the ``replicaSet`` URI option: - -.. code-block:: ruby - - Mongo::Client.new([ '127.0.0.1:27017', '127.0.0.1:27018' ], - database: 'mydb', replica_set: 'myapp') - - # Or using the URI syntax: - Mongo::Client.new("mongodb://127.0.0.1:27017,127.0.0.1:27018/mydb?replicaSet=myapp") - -If the deployment is not a replica set or uses a different replica set name, -all operations will fail (until the expected replica set is returned by -the servers). - -It is also possible to force a replica set connection without specifying -the replica set name. Doing so is generally unnecessary and is deprecated: - -.. code-block:: ruby - - Mongo::Client.new([ '127.0.0.1:27017', '127.0.0.1:27018' ], - database: 'mydb', connect: :replica_set) - - # Or using the URI syntax: - Mongo::Client.new("mongodb://127.0.0.1:27017,127.0.0.1:27018/mydb?connect=replica_set") - -To connect to a MongoDB Atlas cluster which is deployed as a replica set, -connect to the URI: - -.. code-block:: ruby - - Mongo::Client.new("mongodb+srv://username:myRealPassword@cluster0.mongodb.net/test?w=majority") - -Please review the :ref:`SRV URI notes ` if using SRV URIs. - - -.. _connect-sharded-cluster: - -Sharded Cluster Connection --------------------------- - -To connect to a :manual:`sharded cluster` deployment, specify -the addresses of the ``mongos`` routers: - -.. code-block:: ruby - - Mongo::Client.new([ '1.2.3.4:27017', '1.2.3.5:27017' ], database: 'mydb') - - Mongo::Client.new("mongodb://1.2.3.4:27017,1.2.3.5:27017/mydb") - -Note that unlike a replica set connection, you may choose to connect to a -subset of the ``mongos`` routers that exist in the deployment. The driver -will monitor each router and will use the ones that are available -(i.e., the driver will generally handle individual routers becoming -unavailable due to failures or maintenance). When specifying the list of -routers explicitly, the driver will not discover remaining routers that -may be configured and will not attempt to connect to them. - -The driver will automatically balance the operation load among the routers -it is aware of. - -To connect to a MongoDB Atlas cluster which is deployed as a sharded cluster, -connect to the URI: - -.. code-block:: ruby - - Mongo::Client.new("mongodb+srv://username:myRealPassword@cluster0.mongodb.net/test?w=majority") - -When the driver connects to a sharded cluster via an SRV URI, it will -periodically poll the SRV records of the address specified in the URI -for changes and will automatically add and remove the ``mongos`` hosts -to/from its list of servers as they are added and removed to/from the -sharded cluster. - -To force a sharded cluster connection, use the ``connect: :sharded`` -option. Doing so is generally unnecessary and is deprecated: - -.. code-block:: ruby - - Mongo::Client.new([ '127.0.0.1:27017', '127.0.0.1:27018' ], - database: 'mydb', connect: :sharded) - - # Or using the URI syntax: - Mongo::Client.new("mongodb://127.0.0.1:27017,127.0.0.1:27018/mydb?connect=sharded") - -Please review the :ref:`SRV URI notes ` if using SRV URIs. - - -.. _direct-connection: - -Direct Connection ------------------ - -To disable the deployment type discovery and force all operations to be -performed on a particular server, specify the ``direct_connection`` option: - -.. code-block:: ruby - - Mongo::Client.new([ '1.2.3.4:27017' ], database: 'mydb', direct_connection: true) - - # Or using the URI syntax: - Mongo::Client.new("mongodb://1.2.3.4:27017/mydb?directConnection=true") - -Alternatively, the deprecated ``connect: :direct`` option is equivalent: - -.. code-block:: ruby - - Mongo::Client.new([ '1.2.3.4:27017' ], database: 'mydb', connect: :direct) - - # Or using the URI syntax: - Mongo::Client.new("mongodb://1.2.3.4:27017/mydb?connect=direct") - -The direct connection mode is most useful for performing operations on a -particular replica set node, although it also permits the underlying server -to change type (e.g. from a replica set node to a ``mongos`` router, or vice -versa). - - -.. _load-balancer-connection: - -Load Balancer Connection ------------------------- - -Unlike other deployment types, the driver does not currently automatically -detect a load-balanced deployment. - -To connect to a load balancer, specify the ``load_balanced: true`` Ruby option -or the ``loadBalanced=true`` URI option: - -.. code-block:: ruby - - Mongo::Client.new([ '1.2.3.4:27017' ], database: 'mydb', load_balanced: true) - - # Or using the URI syntax: - Mongo::Client.new("mongodb://1.2.3.4:27017/mydb?loadBalanced=true") - -When using these options, if the specified server is not a load balancer, -the client will fail all operations (until the server becomes a load balancer). - -To treat the server as a load balancer even if it doesn't identify as such, -use the ``connect: :load_balanced`` Ruby option or the ``connect=load_balanced`` -URI option: - -.. code-block:: ruby - - Mongo::Client.new([ '1.2.3.4:27017' ], - database: 'mydb', load_balanced: true, connect: :load_balanced) - - # Or using the URI syntax: - Mongo::Client.new("mongodb://1.2.3.4:27017/mydb?loadBalanced=true&connect=load_balanced") - -MongoDB Atlas Connection ------------------------- - -To connect to a MongoDB deployment on Atlas, first create a ``Mongo::Client`` instance using your -cluster's connection string and other client options. - -You can set the `Stable API `_ version as -a client option to avoid breaking changes when you upgrade to a new server version. - -The following code shows how you can specify the connection string and the Stable API client option -when connecting to a MongoDB deployment and verify that the connection is successful: - -.. code-block:: ruby - - require 'mongo' - - # Replace the placeholders with your credentials - uri = "mongodb+srv://:@cluster0.sample.mongodb.net/?retryWrites=true&w=majority" - - # Set the server_api field of the options object to Stable API version 1 - options = { server_api: { version: "1" } } - - # Create a new client and connect to the server - client = Mongo::Client.new(uri, options) - - # Send a ping to confirm a successful connection - begin - admin_client = client.use('admin') - result = admin_client.database.command(ping: 1).documents.first - puts "Pinged your deployment. You successfully connected to MongoDB!" - rescue Mongo::Error::OperationFailure => ex - puts ex - ensure - client.close - end - -Connect to MongoDB Atlas from AWS Lambda ----------------------------------------- - -To learn how to connect to Atlas from AWS Lambda, see the -`Manage Connections with AWS Lambda `__ -documentation. - -.. _srv-uri-notes: - -SRV URI Notes -============= - -When the driver connects to a -:manual:`mongodb+srv protocol ` -URI, keep in mind the following: - -1. SRV URI lookup is performed synchronously when the client is constructed. - If this lookup fails for any reason, client construction will fail with an - exception. When a client is constructed with a list of hosts, the driver - will attempt to contact and monitor those hosts for as long as the client - object exists. If one of these hosts does not resolve initially but becomes - resolvable later, the driver will be able to establish a connection to such - a host when it becomes available. The initial SRV URI lookup must succeed - on the first attempt; subsequent host lookups will be retried by the driver - as needed. -2. The driver looks up URI options in the DNS TXT records corresponding to the - SRV records. These options can be overridden by URI options specified in the - URI and by Ruby options, in this order. -3. Because the URI options are retrieved in a separate DNS query from the - SRV lookup, in environments with unreliable network connectivity - the URI option query may fail when the SRV lookup succeeds. Such a failure - would cause the driver to use the wrong auth source leading to - authentication failures. This can be worked around by explicitly specifying - the auth source: - - .. code-block:: ruby - - Mongo::Client.new("mongodb+srv://username:myRealPassword@cluster0.mongodb.net/test?w=majority&authSource=admin") - -4. If the topology of the constructed ``Client`` object is unknown or a - sharded cluster, the driver will begin monitoring the specified SRV DNS - records for changes and will automatically update the list of servers in the - cluster. The updates will stop if the topology becomes a single or a replica - set. - - -.. _client-options: - -Client Options -============== - -``Mongo::Client``'s constructor accepts a number of options configuring the -behavior of the driver. The options can be provided in the options hash as -Ruby options, in the URI as URI options, or both. If both a Ruby option and -the analogous URI option are provided, the Ruby option takes precedence. - - -Ruby Options ------------- - -.. note:: - - The options passed directly should be symbols. - -.. note:: - - Unless otherwise specified, Ruby options that deal with times are given in - seconds. - -.. list-table:: - :header-rows: 1 - :widths: 25 40 10 15 - - * - Option - - Description - - Type - - Default - - * - ``:app_name`` - - Application name that is printed to the mongod logs upon establishing a connection - in server versions >= 3.4. - - ``String`` - - none - - * - ``:auth_mech`` - - Specifies the authenticaion mechanism to use. Can be one of: - ``:gssapi``, ``:mongodb_cr``, ``:mongodb_x509``, ``:plain``, - ``:scram``, ``:scram256``. GSSAPI (Kerberos) authentication - :ref:`requires additional dependencies `. - - ``Symbol`` - - If user credentials are not supplied, ``nil``. If user credentials - are supplied, the default depends on server version. - MongoDB 4.0 and later: ``:scram256`` if user credentials correspond - to a user which supports SCRAM-SHA-256 authentication, otherwise - ``:scram``. - MongoDB 3.0-3.6: ``:scram``. - MongoDB 2.6: ``:mongodb_cr`` - - * - ``:auth_mech_properties`` - - Provides additional authentication mechanism properties. - - The keys in properties are interpreted case-insensitively. - When the client is created, keys are lowercased. - - - ``Hash`` - - When using the GSSAPI authentication mechanism, the default properties - are ``{service_name: "mongodb"}``. Otherwise the default is nil. - - * - ``:auth_source`` - - Specifies the authentication source. - - ``String`` - - For MongoDB 2.6 and later: **admin** if credentials are - supplied, otherwise the current database - - * - ``:auto_encryption_options`` - - A ``Hash`` of options for configuring automatic encryption. - - - ``:key_vault_client`` - A client connected to the MongoDB instance - storing the encryption data keys (``Mongo::Client``, defaults to the - top-level client instance). - - ``:key_vault_namespace`` - The namespace of the key vault collection - in the format ``"database.collection"`` (``String``, required). - - ``:kms_providers`` - Key management service configuration information. - One or both of the keys ``:local`` and ``:aws`` must be specified - (``Hash``, required). See the "The ``kms_providers`` option`` section of the - :ref:`Client-Side Encryption tutorial` for more - information about this option. - - ``:schema_map`` - The JSONSchema for one or more collections specifying - which fields should be encrypted (``Hash``, optional, defaults to ``nil``). - - ``:bypass_auto_encryption`` - Whether to skip automatic encryption when - performing database operations (``Boolean``, defaults to ``false``). - - ``:extra_options`` - Options related to spawning mongocryptd (``Hash``, - optional, defaults to ``nil``). - - For more information about formatting these options, see the - "Auto-Encryption Options" section of the :ref:`Client-Side Encryption tutorial`. - - ``Hash`` - - none - - * - ``:bg_error_backtrace`` - - Experimental. Controls whether and how backtraces are logged when - errors occur in background threads. If ``true``, the driver will log - complete backtraces. If set to a positive integer, the driver will - log up to that many backtrace lines. If set to ``false`` or ``nil``, - no backtraces will be logged. Other values are an error. - - ``true``, ``false``, ``nil``, ``Integer`` - - none - - * - ``:compressors`` - - A list of potential compressors to use, in order of preference. - Please see below for details on how the driver implements compression. - - ``Array`` - - none - - * - ``:connect`` - - **Deprecated.** Disables deployment topology discovery normally - performed by the dirver and forces the cluster topology to a specific - type. Valid values are ``:direct``, ``:load_balanced``, - ``:replica_set`` or ``:sharded``. If ``:load_balanced`` is used, - the client will behave as if it is connected to a load balancer - regardless of whether the server(s) it connects to advertise themselves - as load balancers. - - ``Symbol`` - - none - - * - ``:connect_timeout`` - - The number of seconds to wait to establish a socket connection - before raising an exception. This timeout is also used for SRV DNS - record resolution. ``nil`` and ``0`` mean no timeout. - Client creation will fail with an error if an invalid timeout value - is passed (such as a negative value or a non-numeric value). - - ``Float`` - - 10 - - * - ``:database`` - - The name of the database to connect to. - - ``String`` - - admin - - * - ``:direct_connection`` - - Connect directly to the specified host, do not discover deployment - topology. - - ``Boolean`` - - false - - * - ``:heartbeat_frequency`` - - The number of seconds for the server monitors to refresh - server states asynchronously. - - ``Float`` - - 10 - - * - ``:id_generator`` - - A custom object to generate ids for documents. Must respond to #generate. - - ``Object`` - - none - - * - ``:load_balanced`` - - Whether to expect to connect to a load balancer. - - ``Boolean`` - - false - - * - ``:local_threshold`` - - Specifies the maximum latency in seconds between the nearest - server and the servers that can be available for selection to operate on. - - ``Float`` - - 0.015 - - * - ``:logger`` - - A custom logger. - - ``Object`` - - ``Logger`` - - * - ``:max_connecting`` - - The maximum number of connections that the connection pool will try to establish in parallel. - - ``Integer`` - - 2 - - * - ``:max_idle_time`` - - The maximum time, in seconds, that a connection can be idle before it - is closed by the connection pool. - - *Warning:* when connected to a load balancer, the driver uses existing - connections for iterating cursors (which includes change streams) - and executing transactions. Setting an idle time via this option may - cause the driver to close connections that are needed for subsequent - operations, causing those operations to fail. - - ``Integer`` - - none - - * - ``:max_pool_size`` - - The maximum size of the connection pool for each server. - Setting this option to zero removes the max size limit from the connection pool, permitting it to grow to any number of connections. - - ``Integer`` - - 20 - - * - ``:max_read_retries`` - - The maximum number of read retries, when legacy read retries are used. - Set to 0 to disable legacy read retries. - - ``Integer`` - - 1 - - * - ``:max_write_retries`` - - The maximum number of write retries, when legacy write retries are used. - Set to 0 to disable legacy write retries. - - ``Integer`` - - 1 - - * - ``:min_pool_size`` - - The minimum number of connections in the connection pool for each - server. The driver will establish connections in the background until - the pool contains this many connections. - - ``Integer`` - - 0 - - * - ``:monitoring`` - - The monitoring object. - - ``Object`` - - none - - * - ``:password`` - - The password of the user to authenticate with. - - ``String`` - - none - - * - ``:platform`` - - Platform information to include in the metadata printed to the mongod logs upon establishing a - connection in server versions >= 3.4. - - ``String`` - - none - - * - ``:read`` - - Specifies the read preference mode and tag sets for selecting servers - as a ``Hash``. Allowed Keys in the hash are ``:mode``, ``:tag_sets`` and - ``:max_staleness``. - - .. code-block:: ruby - - { read: - { mode: :secondary, - tag_sets: [ "data_center" => "berlin" ], - max_staleness: 5, - } - } - - If tag sets are provided, they must be an array of hashes. A server - satisfies the read preference if its tags match any one hash in the - provided tag sets. - - Each tag set must be a hash, and will be converted internally to - a ``BSON::Document`` instance prior to being used for server selection. - Hash keys can be strings or symbols. The keys are case sensitive. - Hash values must be strings, and are matched exactly against the values - in the replica set configuration. - - - ``Hash`` - - ``{ :mode => :primary }`` - - * - ``:read_concern`` - - Specifies the read concern options. The only valid key is ``level``, - for which the valid values are ``:local``, ``:majority``, and - ``:snapshot``. - - ``Hash`` - - none - - * - ``:read_retry_interval`` - - The interval, in seconds, in which reads on a mongos are retried. - - ``Integer`` - - 5 - - * - ``:replica_set`` - - When connecting to a replica set, this is the name of the set to - filter servers by. - - ``String`` - - none - - * - ``:retry_writes`` - - If a single-statement write operation fails from a network error, the driver automatically retries it once - when connected to server versions 3.6+. - - ``Boolean`` - - true - - * - ``:sdam_proc`` - - Since the client begins monitoring the deployment in background as - soon as it is constructed, constructing a client and then subscribing - to :ref:`SDAM ` events in a separate statement may result in the - subscriber not receiving some of the SDAM events. The ``:sdam_proc`` - option permits adding event subscribers on the client being constructed - before any SDAM events are published. - - Pass a ``Proc`` which will be called with the ``Client`` as the argument - after the client's event subscription mechanism has been initialized - but before any of the servers are added to the client. Use this - ``Proc`` to set up SDAM event subscribers on the client. - - Note: the client is not fully constructed when the ``Proc`` provided in - ``:sdam_proc is invoked, in particular the cluster is nil at this time. - ``:sdam_proc`` procedure should limit itself to calling - ``Client#subscribe`` and ``Client#unsubscribe`` methods on on the - passed client only. - - ``Proc`` - - none - - * - ``:server_api`` - - The server API version requested. - This is a hash with the following allowed items: - - ``:version`` (String) - - ``:strict`` (true or false) - - ``:deprecation_errors`` (true or false) - - Note that the server API version can only be specified as a Ruby option, - not as a URI option, and it cannot be overridden for database and - collection objects. - - If server API version is changed on a client (such as via the ``with`` - call), the entire API version hash is replaced with the new specification - (the old and the new individual fields are NOT merged). - - ``Hash`` - - none - - * - ``:server_selection_timeout`` - - The number of seconds to wait for an appropriate server to - be selected for an operation to be executed before raising an exception. - - ``Float`` - - 30 - - * - ``:socket_timeout`` - - The number of seconds to wait for an operation to execute on a - socket before raising an exception. ``nil`` and ``0`` mean no timeout. - Client creation will fail with an error if an invalid timeout value - is passed (such as a negative value or a non-numeric value). - - ``Float`` - - none - - * - ``:srv_max_hosts`` - - The maximum number of mongoses that the driver will communicate with - for sharded topologies. If this option is set to 0, there will - be no maximum number of mongoses. If the given URI resolves - to more hosts than ``:srv_max_hosts``, the client will ramdomly - choose an ``:srv_max_hosts`` sized subset of hosts. Note that the - hosts that the driver ignores during client construction will never - be used. If the hosts chosen by the driver become unavailable, the - client will quit working completely, even though the deployment has - other functional mongoses. - - ``Integer`` - - 0 - - * - ``:srv_service_name`` - - The service name to use in the SRV DNS query. - - ``String`` - - mongodb - - * - ``:ssl`` - - Tell the client to connect to the servers via TLS. - - ``Boolean`` - - false - - * - ``:ssl_ca_cert`` - - The file path containing concatenated certificate authority certificates - used to validate certs passed from the other end of the connection. - One of ``:ssl_ca_cert``, ``:ssl_ca_cert_string`` or ``:ssl_ca_cert_object`` - (in order of priority) is required for ``:ssl_verify``. - - ``String`` - - none - - * - ``:ssl_ca_cert_object`` - - An array of OpenSSL::X509::Certificate representing the certificate - authority certificates used to validate certs passed from the other end - of the connection. One of ``:ssl_ca_cert``, ``:ssl_ca_cert_string`` or - ``:ssl_ca_cert_object`` (in order of priority) is required for ``:ssl_verify``. - - ``Array< OpenSSL::X509::Certificate >`` - - none - - * - ``:ssl_ca_cert_string`` - - A string containing concatenated certificate authority certificates - used to validate certs passed from the other end of the connection. - One of ``:ssl_ca_cert``, ``:ssl_ca_cert_string`` or ``:ssl_ca_cert_object`` - (in order of priority) is required for ``:ssl_verify``. - - ``String`` - - none - - * - ``:ssl_cert`` - - Path to the client certificate file used to identify the application to - the MongoDB servers. The file may also contain the certificate's private - key; if so, the private key is ignored by this option. The file may - also contain intermediate certificates forming the certificate chain - from the client certificate to the CA certificate; any intermediate - certificates will be parsed by the driver and provided to the OpenSSL - context in ``extra_chain_cert`` attribute. If intermediate certificates - are provided, they must follow the client certificate which must be - the first certificate in the file. - - This option, if present, takes precedence over ``:ssl_cert_string`` and - ``:ssl_cert_object`` options. - - ``String`` - - none - - * - ``:ssl_cert_object`` - - The OpenSSL::X509::Certificate used to identify the application to - the MongoDB servers. Only one certificate may be passed through this - option. - - ``OpenSSL::X509::Certificate`` - - none - - * - ``:ssl_cert_string`` - - A string containing the PEM-encoded certificate used to identify the - application to the MongoDB servers. The string may also contain the - certificate's private key; if so, the private key is ignored by this - option. The string may also contain intermediate certificates forming - the certificate chain from the client certificate to the CA certificate; - any intermediate certificates will be parsed by the driver and provided - to the OpenSSL context in ``extra_chain_cert`` attribute. If intermediate - certificates are provided, they must follow the client certificate which - must be the first certificatet in the string. - - This option, if present, takes precedence over the ``:ssl_cert_object`` - option. - - ``String`` - - none - - * - ``:ssl_key`` - - The private keyfile used to identify the connection against MongoDB. Note that even if the key is stored in - the same file as the certificate, both need to be explicitly specified. This option, if present, takes - precedence over the values of :ssl_key_string and :ssl_key_object. - - ``String`` - - none - - * - ``:ssl_key_object`` - - The private key used to identify the connection against MongoDB. - - ``OpenSSL::PKey`` - - none - - * - ``:ssl_key_pass_phrase`` - - A passphrase for the private key. - - ``String`` - - none - - * - ``:ssl_key_string`` - - A string containing the PEM-encoded private key used to identify the - connection against MongoDB. This parameter, if present, takes precedence - over the value of option :ssl_key_object. - - ``String`` - - none - - * - ``:ssl_verify`` - - Whether to perform peer certificate, hostname and OCSP endpoint - validation. Note that the decision of whether to validate certificates - will be overridden if ``:ssl_verify_certificate`` is set, the decision - of whether to validate hostnames will be overridden if - ``:ssl_verify_hostname`` is set and the decision of whether to validate - OCSP endpoint will be overridden if ``:ssl_verify_ocsp_endpoint`` is set. - - ``Boolean`` - - true - - * - ``:ssl_verify_certificate`` - - Whether to perform peer certificate validation. This setting overrides - the ``:ssl_verify`` setting with respect to whether certificate - validation is performed. - - ``Boolean`` - - true - - * - ``:ssl_verify_hostname`` - - Whether to perform peer hostname validation. This setting overrides - the ``:ssl_verify`` setting with respect to whether hostname validation - is performed. - - ``Boolean`` - - true - - * - ``:ssl_verify_ocsp_endpoint`` - - Whether to validate server-supplied certificate against the OCSP - endpoint specified in the certificate, if the OCSP endpoint is specified - in the certificate. This setting overrides :ssl_verify with respect to - whether OCSP endpoint validation is performed. - - ``Boolean`` - - true - - * - ``:truncate_logs`` - - Whether to truncate the logs at the default 250 characters. - - ``Boolean`` - - true - - * - ``:user`` - - The name of the user to authenticate with. - - ``String`` - - none - - * - ``:wait_queue_timeout`` - - The number of seconds to wait for a connection in the connection - pool to become available. - - ``Float`` - - 10 - - * - ``:wrapping_libraries`` - - Information about libraries such as ODMs that are wrapping the driver. - Specify the lower level libraries first. Allowed hash keys: :name, - :version, :platform. Example: ``[name: 'Mongoid', version: '7.1.2']`` - - ``Array`` - - none - - * - ``:write`` - - Deprecated. Equivalent to ``:write_concern`` option. If both ``:write`` - and ``:write_concern`` are specified, their values must be identical. - - - ``Hash`` - - ``{ w: 1 }`` - - * - ``:write_concern`` - - Specifies write concern options as a ``Hash``. - Keys in the hash can be ``:w``, ``:wtimeout``, ``:j``, ``:fsync``. - Note that ``:wtimeout`` is specified in milliseconds, not seconds. - - .. code-block:: ruby - - { write_concern: { w: 2 } } - - - ``Hash`` - - ``{ w: 1 }`` - - * - ``:zlib_compression_level`` - - The Zlib compression level to use, if using compression. See Ruby's Zlib module for valid levels. - - ``Integer`` - - none - -.. note:: - - The Ruby driver does not implement certificate revocation list (CRL) - checking. - - -URI Options ------------ - -Since the URI options are required to be in camel case, which is not the Ruby -standard, the following table shows URI options and their corresponding Ruby -options. - -URI options are explained in detail in the :manual:`Connection URI reference -`. - -.. note:: - - Options that are set in **milliseconds** in the URI are - represented as a ``float`` in Ruby and the units are **seconds**. - -.. list-table:: - :header-rows: 1 - :widths: 40 105 - - * - URI Option - - Ruby Option - - * - appName=String - - ``:app_name => String`` - - * - authMechanism=String - - ``:auth_mech => Symbol`` - - Auth mechanism values are converted as follows from URI options to - Ruby options: - - - ``GSSAPI`` => ``:gssapi`` - - ``MONGODB-CR`` => ``:mongodb_cr`` - - ``MONGODB-X509`` => ``:mongodb_x509`` - - ``PLAIN`` => ``:plain`` - - ``SCRAM-SHA-1`` => ``:scram`` - - ``SCRAM-SHA-256`` => ``:scram256`` - - If a different value is provided for auth mechanism, it is converted - to the Ruby option unmodified and retains its ``String`` type. - Note that, while currently the driver allows a ``Client`` instance - to be constructed with an unrecognized auth mechanism, this behavior - `may change in a future version of the driver `_. - - * - authMechanismProperties=Strings - - ``{ :auth_mech_properties => { :service_realm => String, :canonicalize_host_name => true|false, :service_name => String } }`` - - Specified as comma-separated key:value pairs, e.g. ``"SERVICE_REALM:foo,CANONICALIZE_HOST_NAME:TRUE"``. - - * - authSource=String - - ``:auth_source => String`` - - * - compressors=Strings - - ``:compressors => Array`` - - A comma-separated list of potential compressors to use, in order of - preference. Please see below for details on how the driver implements - compression. - - * - connect=String - - ``:connect => Symbol`` - - The same values that the ``:connect`` Ruby option accepts are - accepted here. For multi-word values, the values must be provided - using underscores to separate the words, i.e. - ``connect=replica_set`` and ``connect=load_balanced``. - - * - connectTimeoutMS=Integer - - ``:connect_timeout => Float`` - - Unlike the corresponding Ruby option which fails client creation on - invalid values (e.g. negative and non-numeric values), invalid values - provided via this URI option are ignored with a warning. - - * - directConnection=Boolean - - ``:direct_connection => Boolean`` - - * - fsync=Boolean - - ``{ :write_concern => { :fsync => true|false }}`` - - * - heartbeatFrequencyMS=Integer - - ``:heartbeat_frequency => Float`` - - * - journal=Boolean - - ``{ :write_concern => { :j => true|false }}`` - - * - loadBalanced=Boolean - - ``:load_balanced => Boolean`` - - * - localThresholdMS=Integer - - ``:local_threshold => Float`` - - * - maxConnecting=Integer - - ``:max_connecting => Integer`` - - * - maxIdleTimeMS=Integer - - ``:max_idle_time => Float`` - - * - maxStalenessSeconds=Integer - - ``{ :read => { :max_staleness => Integer }}`` - - If the maxStalenessSeconds URI option value is -1, the driver treats - this as if the option was not given at all. Otherwise, - if the option value is numeric, the Ruby option is set to the - specified value converted to an ``Integer``. - Note that numeric values greater than 0 but less than 90, or less than - -1, are accepted by the ``Client`` constructor but will cause server - selection to fail (unless the option is changed via, for example, the - ``with`` method prior to any operations being performed on the driver). - If the option value is non-numeric, it is ignored and the driver - treats this case as if the option was not given at all. - - * - maxPoolSize=Integer - - ``:max_pool_size => Integer`` - - * - minPoolSize=Integer - - ``:min_pool_size => Integer`` - - * - readConcernLevel=String - - ``:read_concern => Hash`` - - * - readPreference=String - - ``{ :read => { :mode => Symbol }}`` - - * - readPreferenceTags=Strings - - ``{ :read => { :tag_sets => Array }}`` - - Each instance of the readPreferenceTags field is a comma-separated key:value pair which will appear in the :tag_sets array in the order they are specified. For instance, ``"readPreferenceTags=dc:ny,rack:1&readPreferenceTags=dc:ny"`` will be converted to ``[ { 'dc' => 'ny', 'rack' => '1' }, { 'dc' => 'ny' }]``. - - * - replicaSet=String - - ``:replica_set => String`` - - * - retryWrites=Boolean - - ``:retry_writes => boolean`` - - * - serverSelectionTimeoutMS=Integer - - ``:server_selection_timeout => Float`` - - * - socketTimeoutMS=Integer - - ``:socket_timeout => Float`` - - Unlike the corresponding Ruby option which fails client creation on - invalid values (e.g. negative and non-numeric values), invalid values - provided via this URI option are ignored with a warning. - - * - srvMaxHosts=Integer - - ``:srv_max_hosts => Integer`` - - * - srvServiceName=String - - ``:srv_service_name => String`` - - * - ssl=Boolean - - ``:ssl => true|false`` - - * - tls=Boolean - - ``:ssl => boolean`` - - * - tlsAllowInvalidCertificates=Boolean - - ``:ssl_verify_certificate => boolean`` - - Because ``tlsAllowInvalidCertificates`` uses ``true`` to signify that - verification should be disabled and ``ssl_verify_certificate`` uses - ``false`` to signify that verification should be disabled, the boolean - is inverted before being used to set ``ssl_verify_certificate``. - - * - tlsAllowInvalidHostnames=Boolean - - ``:ssl_verify_hostname => boolean`` - - Because ``tlsAllowInvalidHostnames`` uses ``true`` to signify that - verification should be disabled and ``ssl_verify_hostname`` uses - ``false`` to signify that verification should be disabled, the boolean - is inverted before being used to set ``ssl_verify_hostname``. - - * - tlsCAFile=String - - ``:ssl_ca_cert => String`` - - * - tlsCertificateKeyFile=String - - ``:ssl_cert => String`` - - * - tlsCertificateKeyFile=String - - ``:ssl_key => String`` - - * - tlsCertificateKeyFilePassword=String - - ``:ssl_key_pass_phrase => String`` - - * - tlsDisableOCSPEndpointCheck=Boolean - - ``:ssl_verify_ocsp_endpoint => boolean`` - - Because ``tlsDisableOCSPEndpointCheck`` uses ``true`` to signify that - verification should be disabled and ``ssl_verify_ocsp_endpoint`` uses - ``false`` to signify that verification should be disabled, the boolean - is inverted before being used to set ``ssl_verify_ocsp_endpoint``. - - * - tlsInsecure=Boolean - - ``:ssl_verify => boolean`` - - Because tlsInsecure uses ``true`` to signify that verification should - be disabled and ``ssl_verify`` uses ``false`` to signify that - verification should be disabled, the boolean is inverted before being - used to set ``ssl_verify``. - - * - w=Integer|String - - ``{ :write_concern => { :w => Integer|String }}`` - - * - waitQueueTimeoutMS=Integer - - ``:wait_queue_timeout => Float`` - - * - wtimeoutMS=Integer - - ``{ :write_concern => { :wtimeout => Integer }}`` - - * - zlibCompressionLevel=Integer - - ``:zlib_compression_level => Integer`` - -.. note:: - - The Ruby driver only fails connections when it receives a definitive signed - response indicating that the server's certificate has been revoked. - Because of this, the driver does not recognize the - ``tlsDisableCertificateRevocationCheck`` URI option. If this option is - provided in a URI, it will be ignored. - - -Timeout Options -=============== - -``server_selection_timeout`` ----------------------------- - -When executing an operation, the number of seconds to wait for the driver -to find an appropriate server to send an operation to. Defaults to 30. - -A value of 0 means no timeout. - -When an invalid value (e.g. a negative value or a non-numeric value) is passed -via the URI option, the invalid input is ignored with a warning. When an -invalid value is passed directly to Client via a Ruby option, Client -construction fails with an error. - -In replica set deployments, this timeout should be set to exceed the typical -:manual:`replica set election times ` -in order for the driver to transparently handle primary changes. This timeout -also allows the application and the database to be started simultaneously; -the application will wait up to this much time for the database to become -available. - -If the application server is behind a reverse proxy, server selection timeout -should be lower than the request timeout configured on the reverse proxy (for -example, this applies to deployments on Heroku which has a fixed 30 second -timeout in the routing layer). In development this value can be lowered to -provide quicker failure when the server is not running. - -``socket_timeout`` ------------------- - -The number of seconds to wait for a socket read or write to complete on -regular (non-monitoring) connections. Default is no timeout. - -A value of 0 means no timeout. - -When an invalid value (e.g. a negative value or a non-numeric value) is passed -via the URI option, the invalid input is ignored with a warning. When an -invalid value is passed directly to Client via a Ruby option, Client -construction fails with an error. - -This timeout should take into account both network latency and operation -duration. For example, setting this timeout to 5 seconds will abort queries -taking more than 5 seconds to execute on the server with ``Mongo::Error::SocketTimeoutError``. - -Note that even though by default there is no socket timeout set, the -operating system may still time out read operations depending on its -configuration. The keepalive settings are intended to detect broken network -connections (as opposed to aborting operations simply because they take a -long time to execute). - -Note that if an operation is timed out by the driver due to exceeding the -``socket_timeout`` value, it is not aborted on the server. For this reason -it is recommended to use ``max_time_ms`` option for potentially long running -operations, as this will abort their execution on the server. - -This option does not apply to monitoring connections. - -``connect_timeout`` -------------------- - -The number of seconds to wait for a socket connection to be established to -a server. Defaults to 10. - -This timeout is also used as both connect timeout and socket timeout for -monitoring connections. - -When using a ``mongodb+srv://`` URI, this timeout is also used for SRV and TXT -DNS lookups. Note that the timeout applies per lookup; due to DNS suffix search -lists, multiple lookups may be performed as part of a single name resolution. - -``wait_queue_timeout`` -`````````````````````` - -The number of seconds to wait for a connection in the connection pool to -become available. Defaults to 10. - -As of driver version 2.11, this timeout should be set to a value at least -as large as ``connect_timeout`` because connection pool now fully establishes -connections prior to returning them, which may require several network -round trips. - -``max_time_ms`` ---------------- - -Specified as an option on a particular operation, the number of milliseconds -to allow the operation to execute for on the server. Not set by default. - -Consider using this option instead of a ``socket_timeout`` for potentially -long running operations to be interrupted on the server when they take too -long. - -``wtimeout`` ------------- - -The number of milliseconds to wait for a write to be acknowledged by the -number of servers specified in the write concern. Not set by default, which -instructs the server to apply its default. This option can be set globally -on the client or passed to individual operations under ``:write_concern``. - - -TLS Connections -=============== - -To connect to the MongoDB deployment using TLS: - -- Enable TLS connections in ``Mongo::Client``. -- Specify the client TLS certificate. -- Specify the CA certificate to verify the server's TLS certificate. - -.. note:: - - When using JRuby, ECDSA certificates are not currently supported. - -TLS vs SSL Option Names ------------------------ - -All MongoDB server versions supported by the Ruby driver (2.6 and higher) -only implement TLS. 2.6 and higher servers do not use SSL. - -For historical reasons, the Ruby option names pertaining to TLS configuration -use the ``ssl`` rather than the ``tls`` prefix. The next major version of -the Ruby driver (3.0) will use the ``tls`` prefix for Ruby option names. - -The URI option names use the ``tls`` prefix, with one exception: there is -a ``ssl`` URI option that is deprecated and equivalent to the ``tls`` URI -option. - -Enable TLS Connections ----------------------- - -TLS must be explicitly requested on the client side when the deployment -requires TLS connections - there is currently no automatic detection of -whether the deployment requires TLS. - -To request TLS connections, specify the following client options when -constructing a ``Mongo::Client``: - -- The ``:ssl`` Ruby option. -- The ``tls`` URI option. -- The ``ssl`` URI option (deprecated). - -Specify Client TLS Certificate ------------------------------- - -By default, MongoDB server will attempt to verify the connecting clients' -TLS certificates, which requires the clients to specify their TLS certificates -when connecting. This can be accomplished via: - -- The ``:ssl_cert``/``:ssl_cert_object``/``:ssl_cert_string`` and - ``:ssl_key``/``:ssl_key_object``/``:ssl_key_string``/``:ssl_key_pass_phrase`` - Ruby options. -- The ``tlsCertificateKeyFile`` URI option. - -When using the Ruby options, the client TLS certificate and the corresponding -private key may be provided separately. For example, if the certificate is -stored in ``client.crt`` and the private key is stored in ``client.key``, -a ``Mongo::Client`` may be constructed as follows: - -.. code-block:: ruby - - client = Mongo::Client.new(["localhost:27017"], - ssl: true, - ssl_cert: 'path/to/client.crt', - ssl_key: 'path/to/client.key', - ssl_ca_cert: 'path/to/ca.crt', - ) - -``ssl_cert``, ``ssl_cert_string``, ``ssl_key`` and ``ssl_key_string`` Ruby -options also permit the certificate and the key to be provided in the same -file or string, respectively. The files containing both certificate and -private key frequently have the ``.pem`` extension. When both certificate -and the private key are provided in the same file or string, both the -certifcate and the key options must be utilized, as follows: - -.. code-block:: ruby - - client = Mongo::Client.new(["localhost:27017"], - ssl: true, - ssl_cert: 'path/to/client.pem', - ssl_key: 'path/to/client.pem', - ssl_ca_cert: 'path/to/ca.crt', - ) - -When using the URI option, the certificate and the key must be stored in a -file and both must be stored in the same file. Example usage: - -.. code-block:: ruby - - client = Mongo::Client.new( - "mongodb://localhost:27017/?tls=true&tlsCertificateKeyFile=path%2fto%2fclient.pem&tlsCertificateKeyFile=path%2fto%2fca.crt") - -.. note:: - - URI option values must be properly URI escaped. This applies, for example, to - slashes in the paths. - - -.. _modifying-tls-context: - -Modifying ``SSLContext`` ------------------------- -It may be desirable to further configure TLS options in the driver, for example -by enabling or disabling certain ciphers. Currently, the Ruby driver does not -provide a way to do this when initializing a ``Mongo::Client``. - -However, the Ruby driver provides a way to set global "TLS context hooks" -- -these are user-provided ``Proc``s that will be invoked before any TLS socket -connection and can be used to modify the underlying ``OpenSSL::SSL::SSLContext`` -object used by the socket. - -To set the TLS context hooks, add ``Proc``s to the ``Mongo.tls_context_hooks`` -array. This should be done before creating any Mongo::Client instances. -For example, in a Rails application this code could be placed in an initializer. - -.. code-block:: ruby - - Mongo.tls_context_hooks.push( - Proc.new { |context| - context.ciphers = ["AES256-SHA"] - } - ) - - # Only the AES256-SHA cipher will be enabled from this point forward - -Every ``Proc`` in ``Mongo.tls_context_hooks`` will be passed an -``OpenSSL::SSL::SSLContext`` object as its sole argument. These ``Proc``s will -be executed sequentially during the creation of every ``Mongo::Socket::SSL`` object. - -It is possible to assign the entire array of hooks calling ``Mongo.tls_context_hooks=``, -but doing so will remove any previously assigned hooks. It is recommended to use -the ``Array#push`` or ``Array#unshift`` methods to add new hooks. - -It is also possible to remove hooks from ``Mongo.tls_context_hooks`` by storing -a reference to the Procs somewhere else in the application, and then using -``Array#delete_if`` to remove the desired hooks. - -.. warning:: - - TLS context hooks are global and will affect every instance of ``Mongo::Client``. - Any library that allows applications to enable these hooks should expose methods to - modify the hooks (which can be called by the application) rather than - automatically enabling the hooks when the library is loaded. - -Further information on configuring MongoDB server for TLS is available in the -:manual:`MongoDB manual `. - -Using Intermediate Certificates -``````````````````````````````` - -It is possible to use certificate chains for both the client and the server -certificates. When using chains, the certificate authority parameter should -be configured to contain the trusted root certificates only; the intermediate -certificates, if any, should be provided in the server or client certificates -by concatenating them after the leaf server and client certificates, respectively. - -``:ssl_cert`` and ``:ssl_cert_string`` Ruby options, as well as -``tlsCertificateKeyFile`` URI option, support certificate chains. -``:ssl_cert_object`` Ruby option, which takes an instance of -``OpenSSL::X509::Certificate``, does not support certificate chains. - -The Ruby driver performs strict X.509 certificate verification, which requires -that both of the following fields are set in the intermediate certificate(s): - -- X509v3 Basic Constraints: CA: TRUE -- Can sign certificates -- X509v3 Key Usage: Key Cert Sign -- Can sign certificates - -More information about these flags can be found `in this Stack Overflow question -`_. - -It is a common pitfall to concatenate intermediate certificates to the root -CA certificates passed in ``tlsCAFile`` / ``ssl_ca_cert`` options. By doing -so, the intermediate certificates are elevated to trusted status and are -themselves not verified against the actual CA root. More information on this -issue is available `in this mailing list post -`_. - -Specify CA Certificate ----------------------- - -The driver will attempt to verify the server's TLS certificate by default, and -will abort the connection if this verification fails. By default, the driver -will use the default system root certificate store as the trust anchor. -To specify the CA certificate that the server's certificate is signed with, -use: - -- The ``:ssl_ca_cert``/``:ssl_ca_cert_string``/``:ssl_ca_cert_object`` - Ruby options -- The ``tlsCAFile`` URI option. - -If any of these options are given, the server's certificate will be verified -only against the specified CA certificate and the default system root -certificate store will not be used. - -To not perform server TLS certificate verification, which is not -recommended, specify the ``ssl_verify: false`` Ruby option or the -``tlsInsecure=true`` URI option. - -Specifying Multiple CA Certificates -``````````````````````````````````` - -The ``:ssl_ca_cert`` Ruby option and ``tlsCAFile`` URI option can be used with -a file containing multiple certificates. All certificates thus referenced -will become trust anchors. - -The ``:ssl_ca_cert_object`` option takes an array of certificates, and thus -can also be used to add multiple certificates as certificate authorities. - -The ``:ssl_ca_cert_string`` option supports specifying only one CA certificate. - -.. warning:: - - Intermediate certificates must not be provided in files specified by the - CA certificate options. Doing so would elevate the intermediate certificates - to the status of root certificates, rather than verifying intermediate - certificates against the root certificates. - - If intermediate certificates need to be used, specify them as part of the - client or server TLS certificate files. - - -.. _ocsp-verification: - -OCSP Verification ------------------ - -If the certificate provided by the server contains an OCSP endpoint URI, -the driver will issue an OCSP request to the specified endpoint to verify the -validity of the certificate. - -The OCSP endpoint check may be disabled by setting the -``:ssl_verify_ocsp_endpoint`` Ruby option to ``false`` or by setting the -``tlsDisableOCSPEndpointCheck`` URI option to ``true`` when creating a client. - -.. note:: - - OCSP endpoint checking is not currently performed when running on JRuby, - since JRuby does not correctly expose the OCSP endpoint URI. - - -IPv4/IPv6 Connections -===================== - -When a client is constructed with ``localhost`` as the host name, it will -attempt an IPv4 connection only (i.e. if ``localhost`` resolves to -``127.0.0.1`` and ``::1``, the driver will only try to connect to -``127.0.0.1``). - -When a client is constructed with hostnames other than ``localhost``, it will -attempt both IPv4 and IPv6 connections depending on the addresses that the -hostnames resolve to. The driver respects the order in which ``getaddrinfo`` -returns the addresses, and will attempt to connect to them sequentially. -The first successful connection will be used. - -The driver does not currently implement the Happy Eyeballs algorithm. - - -TCP Keepalive Configuration -=========================== - -Where allowed by system configuration and the Ruby language runtime, -the driver enables TCP keepalive and, for each of the keepalive parameters -listed below, sets the value of the respective parameter to the specified -value if the system value can be determined and is higher than the -listed driver value: - -- ``tcp_keepalive_time``: 120 seconds -- ``tcp_keepalive_intvl``: 10 seconds -- ``tcp_keepalive_cnt``: 9 probes - -.. note:: - - As of JRuby 9.2.14.0, JRuby does not implement the APIs required to - set the keepalive parameters. When using JRuby, the driver will not be - able to set the keepalive parameters and the system configuration will - be in effect. - -To use lower values, or to change the parameters in environments like JRuby -that do not expose the required APIs, please adjust the parameters at the -system level as described in the `MongoDB Diagnostics FAQ keepalive section -`_. - - -Connection Pooling -================== - -``Mongo::Client`` instances have a connection pool per server that the client -is connected to. The pool creates connections on demand to support concurrent -MongoDB operations issued by the application. There is no thread-affinity -for connections. - -The client instance opens one additional connection per known server -for monitoring the server's state. - -The size of each connection pool is capped at ``max_pool_size``, which defaults -to 5. When a thread in the application begins an operation on MongoDB, it tries -to retrieve a connection from the pool to send that operation on. If there -are some connections available in the pool, it checks out a connection from -the pool and uses it for the operation. If there are no connections available -and the size of the pool is less than the ``max_pool_size``, a new connection -will be created. If all connections are in use and the pool has reached its -maximum size, the thread waits for a connection to be returned to the pool by -another thread. If ``max_pool_size`` is set to zero, there is no limit for the -maximum number of connections in the pool. - -Each pool has a limit on the number of connections that can be concurrently -connecting to a server. This limit is called ``max_connecting`` and defaults to -2. If the number of connections that are currently connecting to a server -reaches this limit, the pool will wait for a connection attempt to succeed or -fail before attempting to create a new connection. If your application -has a large number of threads, you may want to increase ``max_connecting`` to avoid -having threads wait for a connection to be established. - -The number of seconds the thread will wait for a connection to become available -is configurable. This setting, called ``wait_queue_timeout``, is defined in -seconds. If this timeout is reached, a ``Timeout::Error`` is raised. The -default is 1 second. - -As of driver version 2.11, the driver eagerly creates connections up to -``min_pool_size`` setting. Prior to driver version 2.11, the driver always -created connections on demand. In all versions of the driver, once a connection -is established, it will be kept in the pool by the driver as long as the pool -size does not exceed ``min_pool_size``. - -Note that, if ``min_pool_size`` is set to a value greater than zero, the -driver will establish that many connections to secondaries in replica set -deployments even if the application does not perform secondary reads. The -purpose of these connections is to provide faster failover when the primary -changes. - -Here is an example of estimating the number of connections a multi-threaded -application will open: A client connected to a 3-node replica set opens 3 -monitoring sockets. It also opens as many sockets as needed to support a -multi-threaded application's concurrent operations on each server, up to -``max_pool_size``. If the application only uses the primary (the default), -then only the primary connection pool grows and the total connections is at -most 8 (5 connections for the primary pool + 3 monitoring connections). -If the application uses a read preference to query the secondaries, their -pools also grow and the total connections can reach 18 (5 + 5 + 5 + 3). - -The default configuration for a ``Mongo::Client`` works for most applications: - -.. code-block:: ruby - - client = Mongo::Client.new(["localhost:27017"]) - -Create this client **once** for each process, and reuse it for all operations. -It is a common mistake to create a new client for each request, which is very -inefficient and not what the client was designed for. - -To support extremely high numbers of concurrent MongoDB operations within one -process, increase ``max_pool_size``: - -.. code-block:: ruby - - client = Mongo::Client.new(["localhost:27017"], max_pool_size: 200) - -To support extremely high numbers of threads that share the same client -within one process, increase ``max_connecting``: - -.. code-block:: ruby - - client = Mongo::Client.new(["localhost:27017"], max_pool_size: 200, max_connecting: 10) - - -Any number of threads are allowed to wait for connections to become available, -and they can wait the default (1 second) or the ``wait_queue_timeout`` setting: - -.. code-block:: ruby - - client = Mongo::Client.new(["localhost:27017"], wait_queue_timeout: 0.5) - -When ``#close`` is called on a client by any thread, all connections are closed: - -.. code-block:: ruby - - client.close - -Note that when creating a client using the `block syntax <#block-syntax>`_ described above, the client is automatically closed after the block finishes executing. - -.. _forking: - -Usage with Forking Servers -========================== - -.. note:: - - Applications using Mongoid should follow `Mongoid's "Usage with Forking Servers" documentation - `_. - The guidance below is provided for applications using the Ruby driver directly. - -When using the Mongo Ruby driver in a Web application with a forking web server -such as Puma, or when the application otherwise forks, each process (parent and child) -must have its own client connections. This is because: - -1. Background Ruby threads, such as those used by the Ruby MongoDB driver to - monitor connection state, are **not** transferred to the child process. -2. File descriptors like network sockets **are** shared between parent and - child processes, which can cause I/O conflicts. - -Regarding (1), if you do not restart the driver's monitoring threads -on the child process after forking, although your child may initially -appear to function correctly, you will eventually see -``Mongo::Error::NoServerAvailable`` exceptions if/when your MongoDB cluster -state changes, for example due to network errors or a maintenance event. - -Regarding (2), if a child process reuses the parent's file descriptors, you -will see ``Mongo::Error::SocketError`` errors with messages such as -``Errno::EPIPE: Broken pipe`` and ``EOFError: end of file reached``. - -When the Ruby driver is used in a web application, if possible, -we recommend to not create any ``Mongo::Client`` instances in the parent -process (prior to the workers being forked), and instead only create client -instances in the workers. - -Manually Handling Process Forks -------------------------------- - -Certain advanced use cases, such as `Puma's fork_worker option `_, -require ``Mongo::Client`` instances to be open in both the parent -and child processes. In this case, you must handle client -reconnection manually. - -To do this, immediately before forking, close any existing client connections -on your parent process. This will prevent the parent process from experiencing -network and monitoring errors due to the child's reuse of the parent's -file descriptors. - -.. code-block:: ruby - - # Immediately before fork - client.close - -.. note:: - - Calling ``Client#close`` does not disrupt database operations currently in-flight. - Clients will automatically reconnect when you perform new operations. - -Then, immediately after forking, reconnect your clients in the newly -forked child process, which will respawn the driver's monitoring threads. - -.. code-block:: ruby - - # Immediately after fork - client.reconnect - -Most web servers provide hooks that can be used by applications to -perform actions when the worker processes are forked. The recommended -hooks are: - -- For `Puma `_, - use ``before_fork`` and ``on_refork`` to close clients in - the parent process and ``on_worker_boot`` to reconnect in the - child processes. -- For `Unicorn `_, - ``before_fork`` to close clients in the parent process and - ``after_fork`` to reconnect clients in the child processes. -- For `Passenger `_, - ``starting_worker_process`` to reconnect clients in the child processes - (Passenger does not appear to have a pre-fork hook). - -Refer to `Mongoid's "Usage with Forking Servers" documentation -`_ -for further examples. - -Troubleshooting ---------------- - -The client's ``summary`` method returns the current state of the client, -including servers that the client is monitoring and their state. If any of -the servers are not being monitored, this is indicated by the ``NO-MONITORING`` -flag. - -A normally operating client will produce a summary similar to the following: - -.. code-block:: ruby - - client.summary - => "#>, - #>, - #>, - #]>>" - -A client that is missing background threads will produce a summary similar to -the following: - -.. code-block:: ruby - - client.summary - => "#>, - #>, - #>, - #]>>" - - -Retryable Reads -=============== - -The driver implements two mechanisms for retrying reads: modern and legacy. -As of driver version 2.9.0, the modern mechanism is used by default, and the -legacy mechanism is deprecated. - -Modern Retryable Reads ----------------------- - -When the modern mechanism is used, read operations are retried once in the -event of a network error, a "not master" error, or a "node is recovering" error. -The following operations are covered: - -- `Collection#find `_ - and related methods -- `Collection#aggregate `_ -- `Collection#count `_, - `Collection#count_documents `_ -- Change stream helpers: `Collection#watch `_, - `Database#watch `_, - `Client#watch `_ -- Enumeration commands: `Client#list_mongo_databases `_, - `Client#list_databases `_, - `Client#database_names `_, - `Database#collection_names `_, - `Database#collections `_, - `Database#list_collections `_, - `Collection#indexes `_ - -When an operation returns a cursor, only the initial read command can be retried. -``getMore`` operations on cursors are not retried by driver version 2.9.0 or -newer. Additionally, when a read operation is retried, a new server for the -operation is selected; this may result in the retry being sent to a different -server from the one which received the first read. - -The behavior of modern retryable reads is covered in detail by the -`retryable reads specification -`_. - -Note that the modern retryable reads can only be used with MongoDB 3.6 and -higher servers. When used with MongoDB 3.4 and lower servers, Ruby driver -version 2.9.0 and higher will not retry reads by default - the application -must explicitly request legacy retryable reads by setting the -``retry_reads: false`` client option or using ``retryReads=false`` URI option. - -Legacy Retryable Reads ----------------------- - -The legacy read retry behavior of the Ruby driver is available by setting the -``retry_reads: false`` client option or passing the ``retryReads=false`` URI -option to the client. - -When using legacy read retry behavior, the number of retries can be set -by specifying the ``max_read_retries`` client option. When using driver version -2.9.0 or higher, the set of operations which would be retried with legacy -retryable reads is identical to the one described above for modern retryable -reads. In older driver versions the behavior of legacy retryable writes was -different in that some of the operations were not retried. - -As of driver version 2.9.0, legacy read retries perform server selection prior -to retrying the operation, as modern retriable writes do. In older driver -versions read retries would be sent to the same server which the initial read -was sent to. - -Disabling Retryable Reads -------------------------- - -To disable all read retries, set the following client options: -``retry_reads: false, max_read_retries: 0``. - - -Retryable Writes -================ - -The driver implements two mechanisms for retrying writes: modern and legacy. -As of driver version 2.9.0, the modern mechanism is used by default on servers -that support it, and the legacy mechanism is deprecated and disabled by default -on all server versions. - -The following write methods used in day-to-day operations on collections -are subject to write retries: - -- ``collection#insert_one`` -- ``collection#update_one`` -- ``collection#delete_one`` -- ``collection#replace_one`` -- ``collection#find_one_and_update`` -- ``collection#find_one_and_replace`` -- ``collection#find_one_and_delete`` -- ``collection#bulk_write`` (for all single statement ops, i.e. not for ``update_many`` or ``delete_many``) - -Modern Retryable Writes ------------------------ - -The modern mechanism will retry failing writes once when the driver is -connected to a MongoDB 3.6 or higher replica set or a sharded cluster, -because they require an oplog on the serer. Modern mechanism will not retry -writes when the driver is connected to a standalone MongoDB server or -server versions 3.4 or older. - -The following errors will cause writes to be retried: - -- Network errors including timeouts -- "not master" errors -- "node is recovering" errors - -Prior to retrying the write the driver will perform server selection, -since the server that the original write was sent to is likely no longer -usable. - -Legacy Retryable Writes ------------------------ - -If modern retryable writes mechanism is disabled by setting the client -option ``retry_writes: false`` or by using the ``retryWrites=false`` -URI option, the driver will utilize the legacy retryable writes mechanism. -The legacy mechanism retries writes on the same operations as the modern -mechanism. By default the legacy mechanism retries once, like the modern -mechanism does; to change the number of retries, set ``:max_write_retries`` -client option. - -The difference between legacy and modern retry mechanisms is that the -legacy mechanism retries writes for a different set -of errors compared to the modern mechanism, and specifically does not -retry writes when a network timeout is encountered. - -Disabling Retryable Writes --------------------------- - -To disable all write retries, set the following client options: -``retry_writes: false, max_write_retries: 0``. - -Logging -======= - -You can either use the default global driver logger or set your own. To set your own: - -.. code-block:: ruby - - Mongo::Logger.logger = other_logger - -See the `Ruby Logger documentation `_ -for more information on the default logger API and available levels. - -Changing the Logger Level -------------------------- - -To change the logger level: - -.. code-block:: ruby - - Mongo::Logger.logger.level = Logger::WARN - -For more control, a logger can be passed to a client for per-client control over logging. - -.. code-block:: ruby - - my_logger = Logger.new(STDOUT) - Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test', :logger => my_logger ) - -Truncation ----------- - -The default logging truncates logs at 250 characters by default. To turn this off pass an -option to the client instance. - -.. code-block:: ruby - - Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test', :truncate_logs => false ) - - -.. _compression: - -Compression -=========== - -To use wire protocol compression, at least one compressor must be explicitly -requested using either the ``:compressors`` Ruby option or the ``compressors`` -URI option. If no compressors are explicitly requested, the driver will not -use compression, even if the required dependencies for one or more compressors -are present on the system. - -The driver chooses the first compressor of the ones requested that is also -supported by the server. The driver currently supports ``zstd``, ``snappy`` and -``zlib`` compressors. ``zstd`` compressor is recommended as it produces -the highest compression at the same CPU consumption compared to the other -compressors. For maximum server compatibility all three compressors can be -specified, e.g. as ``compressors: ["zstd", "snappy", "zlib"]``. - -``zstd`` compressor requires the -`zstd-ruby `_ library to be installed. -``snappy`` compressor requires the -`snappy `_ library to be installed. -If ``zstd`` or ``snappy`` compression is requested, and the respective -library is not loadable, the driver will raise an error during -``Mongo::Client`` creation. ``zlib`` compression requires the ``zlib`` -standard library extension to be present. - -The server support for various compressors is as follows: - -- ``zstd`` requires and is enabled by default in MongoDB 4.2 or higher. -- ``snappy`` requires MongoDB 3.4 or higher and is enabled by default in - MongoDB 3.6 or higher. -- ``zlib`` requires MongoDB 3.6 or higher and is enabled by default in - MongoDB 4.2 and higher. - - -.. _server-api-parameters: - -Server API Parameters -===================== - -Starting with MongoDB 5.0, applications can request that the server behaves -in accordance with a particular server API version. - -Server API parameters can be specified via the ``:server_api`` option to -``Client``. These parameters cannot be provided via a URI. - -Currently the only defined API version is ``"1"``. It can be requested -as follows: - -.. code-block:: ruby - - client = Mongo::Client.new(['localhost'], server_api: {version: "1"}) - -MongoDB server defines API versions as string values. For convenience, if the -API version is provided as an integer, the Ruby driver will stringify it and -send it to the server as a string: - -.. code-block:: ruby - - client = Mongo::Client.new(['localhost'], server_api: {version: 1}) - -Note that the server may define API versions that are not stringified integers. -Applications must not assume that all legal API versions can be expressed -as integers. - -When a particular API version is requested, operations which are part of that -API version behave as specified in that API version. Operations which are not -part of the specified API version behave as they would had the API version -not been specified at all. Operations whose behavior is subject to the -configured API version are commands including command arguments, queries, -aggregation pipeline stages and arguments. - -Applications may request that the server rejects all operations which are not -part of the specified API version by setting the ``:strict`` option: - -.. code-block:: ruby - - client = Mongo::Client.new(['localhost'], server_api: {version: "1", strict: true}) - -For example, since the ``:tailable`` option is not part of the server API -version 1, the following query would fail: - -.. code-block:: ruby - - client = Mongo::Client.new(['localhost'], server_api: {version: "1", strict: true}) - client['collection'].find({}, tailable: true) - # => Mongo::Error::OperationFailure (BSON field 'FindCommand.tailable' is not allowed with apiStrict:true. (323) (on localhost:27017, modern retry, attempt 1)) - -Applications may request that the server rejects all operations which are -deprecated in the specified API version by setting the ``:deprecation_errors`` -option: - -.. code-block:: ruby - - client = Mongo::Client.new(['localhost'], server_api: {version: "1", deprecation_errors: true}) - -Note that, as of this writing, there are no deprecated operations in API -version ``"1"``. - -If the server API parameters have been defined on a ``Client`` object, -they will be sent by the client as part of each [*]_ executed operation. - -.. [*] ``getMore`` commands and commands in transactions do not accept - API parameters, thus the driver will not send them in these cases. - -MongoDB servers prior to 5.0 do not recognize the API parameters, and will -produce a variety of errors should the application configure them. -The Ruby driver will send the API parameters to all MongoDB 3.6 and newer -servers, but the API parameters should only be configured when the application -is communicating with MongoDB 5.0 or newer servers. The API parameters -cannot be sent to MongoDB 3.4 and older servers that use the legacy wire -protocol; if an application configures the API parameters and connects to -MongoDB 3.4 or older servers, the driver will produce an error on every -operation. - -The :ref:`command helper ` permits the application to -send manually constructed commands to the server. If the client is not -configured with server API parameters, the command helper may be used to -issue commands with API parameters: - -.. code-block:: ruby - - client.database.command( - ping: 1, - apiVersion: "1", - apiStrict: false, - apiDeprecationErrors: false, - ) - -If the client is configured with server API parameters, the command helper -may not be used to issue commands with server API parameters. This includes the -case when the server API parameters provided to the client and to the -command helper are identical. If a client is constructed with server API -parameters, to send different API parameters (or none at all) a new client -must be constructed, either from scratch or using the ``with`` method. - -The server API parameters may only be specified on the client level. -They may not be specified on the database, collection, session, transaction -or individual operation level. - - -Development Configuration -========================= - -Driver's default configuration is suitable for production deployment. -In development, some settings can be adjusted to provide a better developer -experience. - -- ``:server_selection_timeout``: set this to a low value (e.g., ``1``) - if your MongoDB server is running locally and you start it manually. A low - server selection timeout will cause the driver to fail quickly when there is - no server running. - - -Production Configuration -======================== - -Please consider the following when deploying an application using the Ruby -driver in production: - -- As of driver version 2.11, the ``:min_pool_size`` client option is completely - respected - the driver will create that many connections to each server - identified as a standalone, primary or secondary. In previous driver versions - the driver created connections on demand. Applications using ``:min_pool_size`` - will see an increase in the number of idle connections to all servers as of - driver version 2.11, and especially to secondaries in replica set deployments - and to nodes in sharded clusters. -- If the application is reverse proxied to by another web server or a load - balancer, ``server_selection_timeout`` should generally be set to a lower - value than the reverse proxy's read timeout. For exampe, `Heroku request timeout - `_ is 30 seconds and - is not configurable; if deploying a Ruby application using MongoDB to Heroku, - consider lowering server selection timeout to 20 or 15 seconds. - - -.. _feature-flags: - -Feature Flags -============= - -The following is a list of feature flags that the Mongo Ruby Driver provides: - -.. list-table:: - :header-rows: 1 - :widths: 30 60 - - * - Flag - - Description - * - ``broken_view_aggregate`` - - When this flag is off, an aggregation done on a view will be executed over - the documents included in that view, instead of all documents in the - collection. When this flag is on, the view fiter is ignored and the - aggregation is applied over all of the documents in the view's - collection. (default: true) - * - ``broken_view_options`` - - When this flag is turned off, the view options will be correctly - propagated to the ``aggregate``, ``count``, ``count_documents``, - ``distinct``, and ``estimated_document_count`` mehods. When this flag is - switched on, the view options will be ignored in those methods. - (default: true) - * - ``validate_update_replace`` - - Validates that there are no atomic operators (those that start with $) - in the root of a replacement document, and that there are only atomic - operators at the root of an update document. If this feature flag is on, - an error will be raised on an invalid update or replacement document, - if not, a warning will be output to the logs. (default: false) - -These feature flags can be set directly on the ``Mongo`` module or using -the ``options`` method: - -.. code:: - - Mongo.validate_update_replace = true - Mongo.options = { validate_update_replace: true } diff --git a/docs/reference/crud-operations.txt b/docs/reference/crud-operations.txt deleted file mode 100644 index 9e638ca029..0000000000 --- a/docs/reference/crud-operations.txt +++ /dev/null @@ -1,1008 +0,0 @@ -*************** -CRUD Operations -*************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -CRUD operations are those which deal with creating, reading, updating, -and deleting documents. - -Key-value Pair Notation -======================= - -Key-value pairs appear in many different contexts in the MongoDB Ruby -driver, and there are some quirks of syntax with regard to how they can -be notated which depend on which version of Ruby you're using. - -When constructing a document, the following syntax is acceptable and -correct for Ruby version 1.9 and later: - -.. code-block:: javascript - - document = { name: "Harriet", age: 36 } - -If you're using Ruby version 2.2 or greater, you can optionally enclose -your keys in quotes. - -.. code-block:: javascript - - document = { "name": "Harriet", "age": 36 } - -If you need to use any MongoDB operator which begins with ``$``, -such as ``$set``, ``$gte``, or ``$near``, you must enclose it in -quotes. If you're using Ruby version 2.2 or greater, you can notate -it as follows: - -.. code-block:: ruby - - collection.update_one({ name: "Harriet" }, { "$set": { age: 42 } }) - -If you're using an earlier version of Ruby, use the hashrocket symbol: - -.. code-block:: ruby - - collection.update_one({ name: "Harriet" }, { "$set" => { age: 42 } }) - -Quoted strings and hashrockets for key-value pairs will work with any -version of Ruby: - -.. code-block:: ruby - - collection.update_one({ "name" => "Harriet" }, { "$set" => { age: 42 } }) - - -Creating Documents -================== - -To insert documents into a collection, select a -collection on the client and call ``insert_one`` or ``insert_many``. - -Insert operations return a ``Mongo::Operation::Result`` object which -gives you information about the insert itself. - -On MongoDB 2.6 and later, if the insert fails, an exception is -raised, because write commands are used. - -On MongoDB 2.4, an exception is only raised if the insert fails and the -:manual:`write concern` is 1 or higher. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - - result = client[:artists].insert_one( { :name => 'FKA Twigs' } ) - result.n # returns 1, because 1 document was inserted. - - result = client[:artists].insert_many([ - { :name => 'Flying Lotus' }, - { :name => 'Aphex Twin' } - ]) - result.inserted_count # returns 2, because 2 documents were inserted. - -.. _specify-decimal128: - -Specify a ``Decimal128`` number -------------------------------- - -.. versionadded:: 3.4 - -:manual:`Decimal128` is a -:doc:`BSON datatype ` -that employs 128-bit decimal-based floating-point values capable -of emulating decimal rounding with exact precision. This -functionality is intended for applications that handle -:manual:`monetary data `, -such as financial and tax computations. - -The following example inserts a value of type ``Decimal128`` into -the ``price`` field of a collection named ``inventory``: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test') - - price = BSON::Decimal128.new("428.79") - client[:inventory].insert_one({ "_id" => 1, - "item" => "26 inch monitor", - "price" => price }) - -The above operation produces the following document: - -.. code-block:: javascript - - { "_id" : 1, "item" : "26 inch monitor", "price" : NumberDecimal("428.79") } - -You can also create a ``Decimal128`` object from a Ruby ``BigDecimal`` -object, or with ``Decimal128.from_string()``. - -.. code-block:: ruby - - big_decimal = BigDecimal.new(428.79, 5) - price = BSON::Decimal128.new(big_decimal) - # => BSON::Decimal128('428.79') - - price = BSON::Decimal128.from_string("428.79") - # => BSON::Decimal128('428.79') - -Query Cache -=========== - -The Ruby driver provides a query cache. When enabled, the query cache will -save the results of find and aggregation queries and return those saved results -when the same queries are performed again. - -To read more about the query cache, visit the -:ref:`query cache tutorial `. - -Reading -======= - -The Ruby driver provides a fluent interface for queries using the ``find`` -method on the collection. Various options are available -to the ``find`` method. - -The query is lazily executed against the server only when iterating the -results - at that point the query is dispatched and a ``Mongo::Cursor`` is -returned. - -To find all documents for a given filter, call ``find`` with the -query: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - - client[:artists].find(:name => 'Flying Lotus').each do |document| - #=> Yields a BSON::Document. - end - -To query nested documents, specify the keys in nested order using dot -notation. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - client[:artists].find("records.releaseYear": 2008).each do |document| - #=> Yields a BSON::Document. - end - - -Legacy ``$query`` Syntax ------------------------- - -*This usage is deprecated.* - -The ``find`` method allows providing the query and the options using the -legacy ``$query`` syntax in the first parameter: - -.. code-block:: ruby - - collection.find(:'$query' => {name: 'Mr. Smith'}) - # Equivalent to: - collection.find(name: 'Mr. Smith') - - collection.find(:'$query' => {name: 'Mr. Smith'}, :'$sort' => {age: 1}) - # Equivalent to: - collection.find(name: 'Mr. Smith').sort(age: 1) - -When the query is executed against MongoDB 3.2 or newer, the driver will -use the protocol appropriate for the server version in question, automatically -converting the query as needed to either a find command or an OP_MSG payload. - - -.. _query-options: - -Query Options -------------- - -To add options to a query, chain the appropriate methods after the -``find`` method. Note that the underlying object, the ``Mongo::Collection::View``, -is immutable and a new object will be returned after each method call. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - - documents = client[:artists].find(:name => 'Flying Lotus').skip(10).limit(10) - documents.each do |document| - #=> Yields a BSON::Document. - end - -The following is a full list of the available options that can be added -when querying and their corresponding methods as examples. - -.. list-table:: - :header-rows: 1 - :widths: 40 80 - - * - Option - - Description - * - ``allow_disk_use`` - - When set to true, the server can write temporary data to disk while - executing the find operation. This option is only available on MongoDB - server versions 4.4 and newer. - * - ``allow_partial_results`` - - For use with sharded clusters. If a shard is down, allows the query - to return results from the shards that are up, potentially only getting - a portion of the results. - * - ``batch_size(Integer)`` - - Specifies the size of each batch of documents the cursor will return on - each ``GETMORE`` operation. - * - ``comment(String)`` - - Adds a comment to the query. - - * - ``explain(**opts)`` - - Returns the query plan for the query. Pass the :manual:`explain options - ` via the keyword arguments using symbol - keys. - - .. code-block:: ruby - - # All server versions - default explain behavior - client[:artists].find.explain - - # MongoDB 3.0 and newer - client[:artists].find.explain(verbosity: :query_planner) - client[:artists].find.explain(verbosity: :execution_stats) - client[:artists].find.explain(verbosity: :all_plans_execution) - - # Alternative syntax using camel case - client[:artists].find.explain(verbosity: "queryPlanner") - client[:artists].find.explain(verbosity: "executionStats") - client[:artists].find.explain(verbosity: "allPlansExecution") - - # MongoDB 2.6 - client[:artists].find.explain(verbose: true) - - The explain operation supports ``:session`` and ``:read`` - (for read preference) options. To specify these options for a single - explain operation, they must be given to the ``find`` method as - follows: - - .. code-block:: ruby - - client[:artists].find({}, session: session).explain - - client[:artists].find({}, read: {mode: :secondary_preferred}).explain - - If the read preference option is specified on the client or on the - collection, it will be passed to the explain operation: - - .. code-block:: ruby - - client[:artists, read: {mode: :secondary_preferred}].find.explain - - Note that the session option is not accepted when creating a collection - object. - - The explain command does not support passing the read concern option. - If the read concern is specifed on the client or collection level, or - if the read concern is specified as a find option, it will NOT be passed - by the driver to the explain command. - - .. note:: - - The information returned by the server for the ``explain`` command - varies with server version and deployment topology. The driver's - ``explain`` method returns whatever the server provided. - - **The return value of ``explain`` method is not part of the driver's - public API and depends on the server version and deployment topology.** - - * - ``hint(Hash)`` - - Provides the query with an - :manual:`index hint` to use. - * - ``let(Hash)`` - - Mapping of :manual:`variables` - to use in the query. - * - ``limit(Integer)`` - - Limits the number of returned documents to the provided value. - * - ``max_scan(Integer)`` - - Sets the maximum number of documents to scan if a full collection scan - would be performed. Deprecated as of MongoDB server version 4.0. - * - ``max_time_ms(Integer)`` - - The maximum amount of time to allow the query to run, in milliseconds. - * - ``no_cursor_timeout`` - - MongoDB automatically closes inactive cursors after a period of 10 - minutes. Call this for cursors to remain open indefinitely on the server. - * - ``projection(Hash)`` - - Specifies the fields to include or exclude from the results. - - .. code-block:: ruby - - client[:artists].find.projection(:name => 1) - - * - ``read(Hash)`` - - Changes the read preference for this query only. - - .. code-block:: ruby - - client[:artists].find.read(:mode => :secondary_preferred) - - * - ``session(Session)`` - - The session to use. - * - ``show_disk_loc(Boolean)`` - - Tells the results to also include the location of the documents on disk. - * - ``skip(Integer)`` - - Skip the provided number of documents in the results. - * - ``snapshot`` - - Execute the query in snapshot mode. Deprecated as of MongoDB server version 4.0. - * - ``sort(Hash)`` - - Specifies sort criteria for the query. - - .. code-block:: ruby - - client[:artists].find.sort(:name => -1) - - -Additional Query Operations ---------------------------- - -``count_documents`` - Get the total number of documents matching a filter, or the total number - of documents in a collection. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - - client[:artists].find(:name => 'Flying Lotus').count_documents - -``estimated_document_count`` - Get an approximate number of documents in the collection. - - Note that unlike ``count_documents``, ``estimated_document_count`` does not - accept a filter. - - The ``count`` server command is used to implement ``estimated_document_count``. - More information can be found via `Count: Behavior `_. - - Due to an oversight in MongoDB versions 5.0.0-5.0.7, the ``count`` command, - which ``estimated_document_count`` uses in its implementation, was not - included in v1 of the Stable API. Therefore, users of the Stable API with - ``estimated_document_count`` are recommended to upgrade their server version to - 5.0.8+ or set ``api_strict: false`` to avoid encountering errors. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - - client[:artists].estimated_document_count - -``count`` - Get an approximate number of documents matching a filter, or an approximate - number of documents in the collection. - - *Deprecated:* The ``count`` method is deprecated and does not work in - transactions. Please use ``count_documents`` to obtain an exact count of - documents potentially matching a filter or ``estimated_document_count`` - to obtain an approximate number of documents in the collection. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - - client[:artists].find(:name => 'Flying Lotus').count - -``distinct`` - Filters out documents with duplicate values. Equivalent to the SQL - ``distinct`` clause. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - - client[:artists].find.distinct(:name ) - -Tailable Cursors ----------------- - -For capped collections you may use a :manual:`tailable cursor -` that remains open -after the client exhausts the results in the initial cursor. The -following code example shows how a tailable cursor might be used: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - client[:artists].drop - client[:artists, capped: true, size: 512].create - - result = client[:artists].insert_many([ - { :name => 'Flying Lotus' }, - { :name => 'Aphex Twin' } - ]) - - enum = client[:artists].find({}, cursor_type: :tailable_await).to_enum - - while true - doc = enum.next - # do something - sleep(1) - end - - -Read Concern ------------- - -Read concern can be :ref:`set on the client ` -or on the collection: - -.. code-block:: ruby - - client = Mongo::Client.new(['localhost:14420'], database: 'music', - read_concern: {level: :local}) - - client['collection'].find.to_a - - collection = client['collection', read_concern: {level: :majority}] - - collection.find.to_a - -The driver does not currently support setting read concern on an individual -query. - -Read concern can be specified when :ref:`starting a transaction -`. When a transaction is active, :manual:`any read concern -specified on the client or on the collection is ignored -`. - -When using the generic command helper, the read concern can be specified as -part of the command: - -.. code-block:: ruby - - client.database.command(dbStats: 1, readConcern: {level: :majority}) - - -.. _read-preference: - -Read Preference ---------------- - -Read preference determines the candidate :manual:`replica set` -members to which a query or command can be sent. They consist of a **mode** -specified as a symbol, an array of hashes known as **tag_sets**, -the ``hedge`` option, which is a Hash specifying hedged read behavior, and two -timing options: **local_threshold** and **server_selection_timeout**. - -``local_threshold`` - Defines the upper limit in seconds of the latency window - between the nearest server and suitable servers to which an operation may be sent. - The default is 15 milliseconds, or 0.015 seconds. - -``server_selection_timeout`` - Defines how long to block for server selection - before throwing an exception. The default is 30,000 milliseconds, or 30 seconds. - -.. note:: - - Read preference does not apply to Standalone deployments. When a client - is connected to a Standalone deployment, any application-specified read - preference is ignored. - -For more information on the algorithm used to select a server, please -refer to the `Server Selection documentation, available on GitHub -`_. - -Read preference can be set as an option on the client or passed an -option when a command is run on a database: - -.. code-block:: ruby - - # Set read preference on a client, used for all operations - client = Mongo::Client.new([ '127.0.0.1:27017' ], - read: { mode: :secondary, - tag_sets: [ { 'dc' => 'nyc' } ] - } ) - - # Set read preference for a given command - client.database.command( { dbStats: 1 }, read: { mode: secondary, - tag_sets: [ { 'dc' => 'nyc' } ] } ) - -Read preference can also be set for specific operations on a collection -using the ``with`` method: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - artists = client[:artists] - artists.with(:read => { :mode => :primary_preferred }).find.to_a - -Mode ----- - -There are five possible read preference modes: ``:primary``, ``:secondary``, -``:primary_preferred``, ``:secondary_preferred`` and``:nearest``. -Please see the :manual:`read preference documentation in the MongoDB Manual -` for an explanation of the modes. - -.. note:: - - When a client is directly connected to a server using the ``:direct_connection`` - Ruby option or the ``directConnection`` URI option, read preference mode - is automatically set to ``:primary_preferred`` to permit read operations - against secondaries. If the application specified a ``:primary`` read - preference mode, the mode is automatically converted to ``:primary_preferred``. - If another read preference mode is specified, it is passed to the server - unchanged. - -Tag sets --------- - -The ``tag_sets`` parameter is an ordered list of tag sets used to -restrict the eligibility of servers for selection, such as for data -center awareness. Please see the :manual:`read preference documentation in -the MongoDB Manual ` for an explanation of tag sets. - - -A read preference tag set (T) matches a server tag set (S) – or -equivalently a server tag set (S) matches a read preference tag set -(T) — if T is a subset of S. - -For example, the read preference tag set ``{ dc: 'ny', rack: 2 }`` -matches a secondary server with tag set ``{ dc: 'ny', rack: 2, size: 'large' }``. - -A tag set that is an empty document matches any server, because -the empty tag set is a subset of any tag set. This means the default -``tag_sets`` parameter ``[{}]`` matches all servers. - -Hedge ------ - -The ``hedge`` parameter is a Hash that specifies whether the server should use -hedged reads. With hedged reads, sharded clusters can route read operations to -two replica set members and return results from the first respondent. - -The ``hedge`` option may only be specified on non-primary read preferences. It -must be provided as Hash with the key ``enabled`` set to ``true`` or ``false``. - -.. code-block:: ruby - - client = Mongo::Client.new( - [ '127.0.0.1:27017' ], - read: { mode: :secondary, hedge: { enabled: true } }, - ) - -See the :manual:`MongoDB Manual ` for -more information about hedged reads. - -.. note:: - - The ``hedge`` option is only available on MongoDB server versions 4.4 and newer. - Attempting to use this option on older server versions will result in an error. - -.. _updating: - -Updating -======== - -Updating documents is possible by executing a single or -multiple update, or by using the ``$findAndModify`` command. - -``update_one`` - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - artists = client[:artists] - - result = artists.find(:name => 'Goldie').update_one("$inc" => { :plays => 1 } ) - result.n # Returns 1. - - result = artists.update_one( { :name => 'Goldie' }, { "$inc" => { :plays => 1 } } ) - result.n # Returns 1. - -``update_many`` - -.. code-block:: ruby - - result = artists.find(:label => 'Hospital').update_many( "$inc" => { :plays => 1 } ) - result.modified_count # Returns the number of documents that were updated. - - result = artists.update_many( { :label => 'Hospital' }, { "$inc" => { :plays => 1 } } ) - result.modified_count # Returns the number of documents that were updated. - -``replace_one`` - -.. code-block:: ruby - - result = artists.find(:name => 'Aphex Twin').replace_one(:name => 'Richard James') - result.modified_count # Returns 1. - - result = artists.replace_one( { :name => 'Aphex Twin' }, { :name => 'Richard James' } ) - result.modified_count # Returns 1. - -To update documents and return a document via ``$findAndModify``, use one of -the three provided helpers: ``find_one_and_delete``, ``find_one_and_replace``, -or ``find_one_and_update``. You can opt to return the document before or after -the modification occurs. - -``find_one_and_delete`` - -.. code-block:: ruby - - client = Mongo::Client.new( [ '127.0.0.1:27017' ], :database => 'music') - artists = client[:artists] - - artists.find(:name => 'José James').find_one_and_delete # Returns the document. - -``find_one_and_replace`` - -.. code-block:: ruby - - doc = artists.find(:name => 'José James').find_one_and_replace(:name => 'José') - doc # Return the document before the update. - - doc = artists.find_one_and_replace({ :name => 'José James' }, { :name => 'José' }) - doc # Return the document before the update. - - doc = artists.find(:name => 'José James'). - find_one_and_replace( { :name => 'José' }, :return_document => :after ) - doc # Return the document after the update. - -``find_one_and_update`` - -.. code-block:: ruby - - doc = artists.find(:name => 'José James'). - find_one_and_update( '$set' => { :name => 'José' } ) - doc # Return the document before the update. - - doc = artists.find_one_and_update( { :name => 'José James' }, { '$set' => { :name => 'José' } } ) - doc # Return the document before the update. - -Update Options --------------- - -To add options to an update command, specify them as key-value pairs in the options -Hash argument. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - artists = client[:artists] - - artists.indexes.create_one(name: 1) - - # Force the server to use the name index to perform this operation - result = artists.update_one( - { :name => 'Goldie' }, - { "$inc" => { :plays => 1 } }, - { hint: { name: 1 } } - ) - result.n # Returns 1. - -The following is a list of the options that can be added to update operations, -including ``update_one``, ``update_many``, ``replace_one``, -``find_one_and_delete``, ``find_one_and_update``, and ``find_one_and_replace``. - -.. list-table:: - :header-rows: 1 - :widths: 40 80 - - * - Option - - Description - * - ``array_filters`` - - An Array of filter documents that determine which array elements to modify - for an update operation on an array field. - * - ``bypass_document_validation`` - - Whether to skip document-level validation before writing the document. - * - ``collation`` - - Specifies a set of rules to use when comparing strings complying with the - conventions of a particular language. - * - ``hint`` - - The index to use for this operation. May be specified as a Hash - (e.g. { _id: 1 }) or as a String (e.g. "_id_"). Supported on MongoDB - server versions 4.2 and newer for ``update_one``, ``update_many``, and - ``replace_one`` commands, and on server versions 4.4 and newer for - ``find_one_and_delete``, ``find_one_and_update``, and ``find_one_and_replace`` - commands. - * - ``let(Hash)`` - - Mapping of :manual:`variables` - to use for this operation. - * - ``projection`` - - The fields to exclude or include in the operation result (only available - on ``find_one_and_delete``, ``find_one_and_replace``, and - ``find_one_and_update`` commands). - * - ``return_document`` - - A symbol specifying whether to return the updated document as it was before or - after the update. Potential values are ``:before`` or ``:after``. - (Only available on ``find_one_and_update`` and ``find_one_and_replace`` commands). - * - ``sort`` - - How to sort the results of a find and modify command. Specified as a Hash - key-value pair, where the key is the name of the field to sort by, and - the value is either 1 or -1, specifying a sort in ascending or descending - order (only available on ``find_one_and_delete``, ``find_one_and_replace``, - and ``find_one_and_update`` commands). - * - ``session`` - - The session to use for this operation. - * - ``upsert`` - - Whether to upsert if the document doesn't exist. Cannot be used on - ``find_one_and_delete`` operation. - -For more information about update options, see the MongoDB server documentation -on the following commands: - -- :manual:`update ` -- :manual:`findAndModify ` - -Deleting -======== - -``delete_one`` - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - artists = client[:artists] - - result = artists.find(:name => 'Björk').delete_one - result.deleted_count # Returns 1. - - result = artists.delete_one(:name => 'Björk') - result.deleted_count # Returns 1. - -``delete_many`` - -.. code-block:: ruby - - result = artists.find(:label => 'Mute').delete_many - result.deleted_count # Returns the number deleted. - - result = artists.delete_many(:label => 'Mute') - result.deleted_count # Returns the number deleted. - -Delete Options --------------- - -To add options to a delete command, specify them as key-value pairs in the -options Hash argument. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - artists = client[:artists] - - artists.indexes.create_one(name: 1) - - # Force the server to use the name index to perform this operation - result = artists.find(:name => 'Björk').delete_one(hint: { name: 1 }) - result.deleted_count # Returns 1. - -The following is a full list of the available options that can be added -to ``delete_one`` and ``delete_many`` operations. - -.. list-table:: - :header-rows: 1 - :widths: 40 80 - - * - Option - - Description - * - ``collation`` - - Specifies a set of rules to use when comparing strings complying with the - conventions of a particular language. - * - ``hint`` - - The index to use for this operation. May be specified as a Hash - (e.g. { _id: 1 }) or as a String (e.g. "_id_"). Supported on MongoDB - server versions 4.4 and newer. - * - ``let(Hash)`` - - Mapping of :manual:`variables` - to use for this operation. - * - ``session`` - - The session to use for this operation. - -For more information about update options, see the MongoDB server documentation -on the :manual:`delete command. ` - -.. _write-concern: - -Write Concern -============= - -All write operations in MongoDB are executed with a write concern which is -the level of acknowledgment requested from MongoDB for the particular write. -More information about write concerns in general is available in the -`MongoDB manual `_. - -The Ruby driver supports specifying write concern on client, collection, -session (for transactions on that session), transaction, GridFS bucket -and write stream levels, as well as when manually issuing commands via -``Database#command``. - -As of driver version 2.10, all driver objects accepting write concerns do so -through the ``:write_concern`` option, which should be given a hash with -the write concern options. Usage of the ``:write`` option is deprecated. -In driver versions 2.9 and below, client, collection and GridFS objects -took write concern options in the ``:write`` option with session and -transaction objects employing the ``:write_concern`` option. - -Below are some examples of passing write concerns to client and collection -objects. The ``:write_concern`` option can be provided when constructing -new client and collection objects, or to the ``#with`` methods. - -GridFS examples are provided on the :ref:`GridFS ` page. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music', - write_concern: {w: 2}) - alt_client = client.with(write_concern: {w: :majority}) - - collection = client[:artists, write_concern: {w: 3}] - alt_collection = collection.with(write_concern: {w: :majority}) - - # Uses w: 3 - collection.insert_one({name: 'SUN Project'}) - # Uses w: :majority - alt_collection.insert_one({name: 'SUN Project'}) - -Driver versions 2.9 and earlier accepted write concerns on client and collection -level via the ``:write`` option. This usage continues to be supported for -backwards compatibility, but is deprecated: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music', - write: {w: 2}) - alt_client = client.with(write: {w: :majority}) - - collection = client[:artists, write: {w: 3}] - alt_collection = collection.with(write: {w: :majority}) - -If both ``:write`` and ``:write_concern`` options are provided, their -values must be identical or an exception will be raised: - -.. code-block:: ruby - - # OK - client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music', - write_concern: {w: 3}, write: {w: 3}) - - # Error - client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music', - write_concern: {w: 3}, write: {w: :majority}) - -When ``#with`` methods are used to alter the options on a client or collection, -the last provided option wins in case of naming differences: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music', - write_concern: {w: 2}) - alt_client = client.with(write: {w: 3}) - - alt_client.options[:write] - # => {"w"=>3} - - alt_client.options[:write_concern] - # => nil - -When using transactions, write concern is only sent to the server in -``commit_transaction`` and ``abort_transaction`` operations -per the `transactions specification -`_. -Write concern may be set via the ``:write_concern`` option in a -``with_transaction`` or ``start_transaction`` call, or via -``default_transaction_options`` option on a session object. -If neither of these is set, write concern of the client is used; note -that transactions ignore write concerns of collections that are involved -in their operations. Note that when setting the write concern as a -transaction option, the ``:write`` option is not recognized by any -driver version. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music', - write_concern: {w: 2}) - collection = client[:artists, write_concern: {w: :majority}] - - - session = client.start_session - session.with_transaction do - collection.insert_one({test: 1}, session: session) - - # Uses w: 2 when committing - end - - - session = client.start_session(default_transaction_options: - {write_concern: {w: 3}) - ) - session.with_transaction do - collection.insert_one({test: 1}, session: session) - - # Uses w: 3 when committing - end - - - session = client.start_session - session.with_transaction(write_concern: {w: 3}) do - collection.insert_one({test: 1}, session: session) - - # Uses w: 3 when committing - end - -When write concerns are inherited, inheritance applies to the entire -write concern hash rather than individual elements. For example, ``j: true`` -is not inherited in the following case: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music', - write_concern: {w: 1, j: true}) - collection = client[:artists, write_concern: {w: 2}] - - collection.write_concern.options - # => #2}> - -Although CRUD operations accept an options hash, they currently do not -recognize the ``:write_concern`` option: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music', - write_concern: {w: 2}) - collection = client[:artists, write_concern: {w: :majority}] - - # Still uses w: :majority - collection.insert_one({name: 'SUN Project'}, write_concern: {w: 1}) - -The easiest workaround for this is to use ``#with`` to obtain a new collection -instance with the desired write concern: - -.. code-block:: ruby - - # Uses w: 1 - collection.with(write_concern: {w: 1}).insert_one(name: 'SUN Project') - -Write concern can also be manually specified in ``Database#command``: - -.. code-block:: ruby - - client.database.command(create: 'foo-collection', writeConcern: {w: :majority}) - -Note that writeConcern here is part of the operation rather than options, -and the syntax is the camel case one that MongoDB server recognizes, not the -underscore one that Ruby driver uses. - -.. _dots-dollars-in-field-names: - -Field Names with Dots/Periods (.) and Dollar Signs ($) -====================================================== - -Starting in Mongo Ruby Driver version 2.18.0, the ability to work with fields -that begin with dollar signs ($) and fields with dots/periods (.) in them is available. -In Driver version 2.17.0 and earlier, any attempt to work with dotted or dollared -fields would result in an ``IllegalKey`` error being raised. See the MongoDB docs -on `Field Names with Periods (.) and Dollar Signs ($) `_ -for more information on working with these types of fields. - -A Note about the BSON Symbol type -================================= - -Because the BSON specification deprecated the BSON symbol type, the ``bson`` gem -will serialize Ruby symbols into BSON strings when used on its own. However, in -order to maintain backwards compatibility with older datasets, the Ruby driver -overrides this behavior to serialize Ruby symbols as BSON symbols. This is -necessary to be able to specify queries for documents which contain BSON -symbols as fields. Despite this, new documents with symbol type fields should -*not* be stored in the database; instead, use string fields. - -To override default behavior and configure the driver to encode symbol values -as strings, include the following code snippet in your project: - -.. code-block:: ruby - - class Symbol - def bson_type - BSON::String::BSON_TYPE - end - end diff --git a/docs/reference/database-tasks.txt b/docs/reference/database-tasks.txt deleted file mode 100644 index 1806f2f9c3..0000000000 --- a/docs/reference/database-tasks.txt +++ /dev/null @@ -1,63 +0,0 @@ -********* -Databases -********* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -The driver provides various helpers on database objects for executing -commands, getting collection lists, and administrative tasks. - - -List Collections -================ - -To get a list of collections or collection names for a database, use -``collections`` and ``collection_names``, respectively. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music') - database = client.database - - database.collections # Returns an array of Collection objects. - database.collection_names # Returns an array of collection names as strings. - - -.. _arbitrary-commands: - -Arbitrary Comands -================= - -To execute any command on the database, use the ``command`` method. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music') - database = client.database - - result = database.command(:ping => 1) - result.first # Returns the BSON::Document returned from the server. - -.. note:: - - Specifying server API version as a client option and also specifying - any of the respective command parameters to the ``command`` method - (i.e. the ``apiVersion``, ``apiStrict`` and ``apiDeprecationErrors`` - command parameters) at the same time is not allowed and will produce an error. - - -Drop Database -============= - -To drop a database, use the ``drop`` method. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - client.database.drop diff --git a/docs/reference/driver-compatibility.txt b/docs/reference/driver-compatibility.txt deleted file mode 100644 index a0aaea71af..0000000000 --- a/docs/reference/driver-compatibility.txt +++ /dev/null @@ -1,699 +0,0 @@ -.. _compatibility: - -******************** -Driver Compatibility -******************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - - -.. _mongodb-compatibility: - -MongoDB Compatibility -===================== - -The following compatibility table specifies the recommended -version(s) of the MongoDB Ruby driver for use with a specific version of -MongoDB. Except when indicated, the specified driver versions expose or -take advantage of the features added in the corresponding server versions. - -MongoDB server releases are generally backwards compatible, meaning a -particular version of the driver will generally work with newer versions of -the server but may not take advantage of the functionality released in the -newer version of the server. - -.. important:: - - MongoDB ensures compatibility between the MongoDB Server and the drivers - for three years after the server version's end of life (EOL) date. To learn - more about the MongoDB release and EOL dates, see - `MongoDB Software Lifecycle Schedules `__. - -The first column lists the driver versions.“D” in other columns means support -for that MongoDB version is deprecated and will be removed in a future driver -version. - -.. list-table:: - :header-rows: 1 - :stub-columns: 1 - :class: compatibility-large no-padding - - * - Ruby Driver - - MongoDB 7.0 - - MongoDB 6.0 - - MongoDB 5.0 - - MongoDB 4.4 - - MongoDB 4.2 - - MongoDB 4.0 - - MongoDB 3.6 - - MongoDB 3.4 - - MongoDB 3.2 - - MongoDB 3.0 - - MongoDB 2.6 - - * - 2.20 - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - * - 2.19 - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - * - 2.18 - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - * - 2.17 - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - * - 2.16 - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - D - - D - - * - 2.15 - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - * - 2.14 - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - * - 2.13 - - - - - - - - |checkmark| [#ocsp]_ - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - * - 2.12 - - - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - * - 2.11 - - - - - - - - - - |checkmark| [#client-side-encryption]_ - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - * - 2.10 - - - - - - - - - - |checkmark| [#srv-polling]_ [#client-side-encryption]_ - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - * - 2.9 - - - - - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - * - 2.8 - - - - - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - * - 2.7 - - - - - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - * - 2.6 - - - - - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - * - 2.5 - - - - - - - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - -.. [#ocsp] OCSP verification is implemented as of driver version 2.14. - -.. [#srv-polling] Polling of SRV records in sharded topologies is - implemented as of driver version 2.11. - -.. [#client-side-encryption] Client-side encryption is implemented as of - driver version 2.12. - -The driver does not support older versions of MongoDB. - - -.. _ruby-compatibility: - -Ruby Compatibility -================== - -The following compatibility table specifies the versions of Ruby supported -by the various versions of the MongoDB Ruby driver. - -The first column lists the driver versions. "D" in a column means support -for that Ruby version is deprecated. - -.. list-table:: - :header-rows: 1 - :stub-columns: 1 - :class: compatibility-large no-padding - - * - Ruby Driver - - Ruby 3.2 - - Ruby 3.1 - - Ruby 3.0 - - Ruby 2.7 - - Ruby 2.6 - - Ruby 2.5 - - Ruby 2.4 - - Ruby 2.3 - - Ruby 2.2 - - Ruby 2.1 - - Ruby 2.0 - - Ruby 1.9 - - JRuby 9.4 - - JRuby 9.3 - - JRuby 9.2 - - JRuby 9.1 - - * - 2.20 - - |checkmark| - - |checkmark| - - |checkmark| - - D - - - - - - - - - - - - - - - - - - |checkmark| - - |checkmark| - - D - - - - * - 2.19 - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - - - - - - - - - - - - - - - |checkmark| - - |checkmark| - - - - * - 2.18 - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - - - - - - - |checkmark| - - |checkmark| - - - - * - 2.17 - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - - - - - - - - - |checkmark| - - - - * - 2.16 - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - - - - - - - - - - - - - - - |checkmark| - - - - * - 2.15 - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - - - - - - - - - - - - - |checkmark| - - - - * - 2.14 - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - - - - - - - - - - - - - |checkmark| - - - - * - 2.13 - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - - - - - |checkmark| - - - - * - 2.12 - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - - - - - |checkmark| - - - - * - 2.11 - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - - - - - |checkmark| - - - - * - 2.10 - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - D - - D - - - - - - |checkmark| - - |checkmark| - - * - 2.9 - - - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - D - - D - - - - - - |checkmark| - - |checkmark| - - * - 2.8 - - - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - |checkmark| - - |checkmark| - - * - 2.7 - - - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - |checkmark| - - |checkmark| - - * - 2.6 - - - - - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - |checkmark| - - |checkmark| - -The driver does not support older versions of Ruby. - - -Rails/ActiveSupport Compatibility -================================= - -The Ruby driver does not depend on ActiveSupport. However, when an -application uses ActiveSupport or Ruby on Rails, -it must load the driver's ActiveSupport -compatibility code for behavior like time serialization to be correct: - -.. code-block:: ruby - - require 'mongo' - require 'mongo/active_support' - -Applications using Mongoid 7.0.6 or newer do not need to explicitly load -the driver's ActiveSupport code, since Mongoid automatically does so. - - -.. _tls-compatibility: - -TLS/SSL Compatibility -===================== - -The driver will utilize the protocols supported by the underlying Ruby -``openssl`` extension. In turn, the ``openssl`` extension generally exposes -the functionality that exists in the operating system's OpenSSL library. - -Industry best practices, and some regulations, require the use of TLS 1.1 -or newer. Some operating systems or versions may not provide an OpenSSL version -new enough to support these TLS versions. - -Users of macOS older than 10.13 (High Sierra) will need to install Ruby from -`rvm`_, `homebrew`_, `macports`_, or another similar source. See -`installation information on ruby-lang.org`_ for more options. - -Users of Linux or other non-macOS Unix can check their OpenSSL version -as follows: - -.. code-block:: sh - - openssl version - -If the version number is less than 1.0.1 support for TLS 1.1 or newer is -not available. Contact your operating system vendor for a solution or upgrade -to a newer distribution. - -You can check your Ruby interpreter by executing the following command: - -.. code-block:: sh - - ruby -e "require 'net/http'; require 'json'; puts JSON.parse(Net::HTTP.get(URI('https://www.howsmyssl.com/a/check')))['tls_version']" - -You should see "TLS 1.X" where X is >= 1. - -You can read more about TLS versions and their security implications `here -`_. - -.. _rvm: https://rvm.io/ -.. _homebrew: https://brew.sh/ -.. _macports: https://www.macports.org/ -.. _installation information on ruby-lang.org: https://www.ruby-lang.org/en/documentation/installation - - -Atlas Compatibility -=================== - -`Driver version 2.6.1 `_ -or higher is recommended when using MongoDB Atlas, as this version has -significant performance improvements when TLS connections are used, and all -Atlas connections use TLS. - -When running on JRuby and connecting to Atlas Free Tier, -`driver version 2.6.4 `_ -or higher and Java 8 or higher are required. - - -``mongo_kerberos`` Compatibility -================================ - -The following compatibility table specifies the version(s) of the -:ref:`mongo_kerberos library ` to use with a specific version of -the driver. - -.. list-table:: - :header-rows: 1 - :stub-columns: 1 - :class: compatibility-large no-padding - - * - Ruby Driver - - mongo_kerberos |nbsp| 2.1 - - * - 2.7 - 2.19 - - |checkmark| - - -JRuby and Kerberos Authentication -================================= - -If the ``mongo_kerberos`` gem is used for Kerberos authentication with JRuby, the the JVM system -property "sun.security.jgss.native" to will be set to "true" in order to facilitate the use of -the system cache of TGTs (e.g. TGTs obtained with ``kinit``). Any other use of the JGSS library -will also be affected by this setting, meaning any TGTs in the system cache will be available for -obtaining Kerberos credentials as well. - -.. include:: /includes/unicode-checkmark.rst -.. include:: /includes/unicode-nbsp.rst - - -JRuby and TLS Connections -========================= - -Due to JRuby limitations: - -- ECDSA server certificates are not supported. -- OCSP endpoint checking is not performed. diff --git a/docs/reference/geospatial-search.txt b/docs/reference/geospatial-search.txt deleted file mode 100644 index 8550d64b31..0000000000 --- a/docs/reference/geospatial-search.txt +++ /dev/null @@ -1,106 +0,0 @@ -***************** -Geospatial Search -***************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -MongoDB offers a number of indexes and query mechanisms to handle -geospatial information. This section demonstrates how to create -and use -:manual:`geospatial indexes` -with the Ruby driver. - -The examples on this page use a sample collection called -``restaurants`` in the ``test`` database. -A `sample dataset `_ -is available for download. - -The following is a sample document in the ``restaurants`` -collection: - -.. code-block:: javascript - - { - "address": { - "building": "1007", - "coord": [ -73.856077, 40.848447 ], - "street": "Morris Park Ave", - "zipcode": "10462" - }, - "borough": "Bronx", - "cuisine": "Bakery", - "grades": [ - { "date": { "$date": 1393804800000 }, "grade": "A", "score": 2 }, - { "date": { "$date": 1299715200000 }, "grade": "B", "score": 14 } - ], - "name": "Morris Park Bake Shop", - "restaurant_id": "30075445" - } - -The following example creates a ``2dsphere`` index on the -``address.coord`` field: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test' ) - client[:restaurants].indexes.create_one( { 'address.coord' => '2dsphere' }) - -Once the index is created, you can use several operators to query -against it, including the -:manual:`$near`, -:manual:`$geoWithin`, and -:manual:`$geoIntersects` -operators. The following example uses the ``$near`` operator to find -all restaurants within 500 meters of the given coordinates. - -.. code-block:: ruby - - client = Mongo::Client.new('mongodb://127.0.0.1:27017/test') - collection = client[:restaurants] - - collection.find( - { 'address.coord' => - { "$near" => - { "$geometry" => - { "type" => "Point", "coordinates" => [ -73.96, 40.78 ] }, - "$maxDistance" => 500 - } - } - } - ).each do |doc| - - #=> Yields a BSON::Document. - - end - -To find all documents with a location within the -perimeter of a given polygon, use the ``$geoWithin`` -operator: - -.. code-block:: ruby - - client = Mongo::Client.new('mongodb://127.0.0.1:27017/test') - collection = client[:restaurants] - - collection.find( - { "address.coord" => - { "$geoWithin" => - { "$geometry" => - { "type" => "Polygon" , - "coordinates" => [ [ [ -73, 40 ], [ -74, 41 ], [ -72, 39 ], [ -73, 40 ] ] ] - } - } - } - } - ).each do |doc| - - #=> Yields a BSON::Document. - - end - diff --git a/docs/reference/gridfs.txt b/docs/reference/gridfs.txt deleted file mode 100644 index 756750b18c..0000000000 --- a/docs/reference/gridfs.txt +++ /dev/null @@ -1,325 +0,0 @@ -.. _gridfs: - -****** -GridFS -****** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -The driver provides a clean and simple interface to work with storage of -chunked files in the database, also known as the pattern "GridFS". The API allows you to either -work with Grid::File objects or with read and write streams. - -Creating a GridFS object ("Grid::FSBucket") -=========================================== - -You can create a GridFS object by calling ``fs`` on a database, with optional -arguments. ``fs`` returns a ``Grid::FSBucket`` object. - -The options that ``Grid::FSBucket`` supports are: - -.. list-table:: - :header-rows: 1 - :widths: 40 80 - - * - Option - - Description - * - ``:bucket_name`` - - The name of the GridFS Bucket. Default is ``fs``. - * - ``:fs_name`` - - The name of the GridFS Bucket. Takes precedence over ``bucket_name``. - Default is ``fs``. - * - ``:chunk_size`` - - Specifies the size of each file chunk in the database. - * - ``:write_concern`` - - The write concern to use when uploading files. Please see the - :ref:`Write Concern ` section under CRUD operations - for how to work with write concerns. - * - ``:write`` - - Deprecated. Same as ``:write_concern``. - * - ``:read`` - - The read preference to use when downloading files. - - -For example, you can create a GridFS bucket object with a particular read preference: - -.. code-block:: ruby - - fs_bucket = database.fs( read: { mode: :secondary } ) - - -Working with write streams -========================== - -To upload a file to GridFS using a write stream, you can either open a stream -and write to it directly or write the entire contents of an ``IO`` object to -GridFS all at once. - -To open an upload stream and write to it: - -.. code-block:: ruby - - File.open('/path/to/my-file.txt', 'r') do |file| - fs_bucket.open_upload_stream('my-file.txt') do |stream| - stream.write(file) - end - end - -To upload the entire contents of an IO object in one call: - -.. code-block:: ruby - - File.open('/path/to/my-file.txt', 'r') do |file| - fs_bucket.upload_from_stream('my-file.txt', file) - end - -Write streams support the following options: - -.. list-table:: - :header-rows: 1 - :widths: 40 80 - - * - Option - - Description - * - ``:chunk_size`` - - Specifies the size of each file chunk in the database. - * - ``:write_concern`` - - The write concern to use when uploading files. Please see the - :ref:`Write Concern ` section under CRUD operations - for how to work with write concerns. - * - ``:write`` - - Deprecated. Same as ``:write_concern``. - -The options can be provided as the last argument to the write stream methods: - -.. code-block:: ruby - - fs_bucket.open_upload_stream('my-file.txt', write_concern: {w: 2}) do |stream| - stream.write_concern - # => #2}> - - # ... - end - - fs_bucket.upload_from_stream('my-file.txt', file, write_concern: {w: 2}) - - -Working with read streams -========================= - -To download a file from GridFS using a read stream, you can either open a -read stream and read from it directly or download the entire file all at once. - -To open a download stream and read from it: - -.. code-block:: ruby - - File.open('/path/to/my-output-file.txt', 'w') do |file| - fs_bucket.open_download_stream(file_id) do |stream| - file.write(stream.read) - end - end - -To download the file all at once and write it to an IO object: - -.. code-block:: ruby - - File.open('/path/to/my-output-file.txt', 'w') do |file| - fs_bucket.download_from_stream(file_id, file) - end - -You can also download a file specified by a name and (optionally) -revision number. Revision numbers are used to distinguish between files -sharing the same name, ordered by date of upload. The revision number passed to -``open_download_stream_by_name`` can be positive or negative. - -.. code-block:: ruby - - File.open('/path/to/my-output-file.txt', 'w') do |file| - fs_bucket.open_download_stream_by_name('my-file.txt', revision: -2) do |stream| - file.write(stream.read) - end - end - -To download the entire contents of the file specified by name and (optionally) -revision number: - -.. code-block:: ruby - - File.open('/path/to/my-output-file.txt', 'w') do |file| - fs_bucket.download_to_stream_by_name('my-file.txt', file, revision: -2) - end - -Read streams support the following options: - -.. list-table:: - :header-rows: 1 - :widths: 40 80 - - * - Option - - Description - * - ``:read`` - - The read preference to use when downloading files. - -Some, but not all, of the read methods listed above pass these options to -the underlying read streams. Please consult the API documentation for each -method to determine whether it supports a particular option. - -Finding file metadata -===================== - -You can retrieve documents containing metadata about files in the GridFS files collection. - -.. code-block:: ruby - - fs_bucket.find(filename: 'my-file.txt') - -Deleting files -============== - -You can delete a file by id. - -.. code-block:: ruby - - fs_bucket.delete(file_id) - - -Working with Grid::File objects -=============================== - -This object can be used to wrap a file to be inserted into the database using -GridFS and the object that is retrieved. - -To create a file with raw data: - -.. code-block:: ruby - - file = Mongo::Grid::File.new('I am a file', :filename => 'new-file.txt') - -To create a file from a Ruby ``File`` object: - -.. code-block:: ruby - - file = File.open('/path/to/my-file.txt') - grid_file = Mongo::Grid::File.new(file.read, :filename => File.basename(file.path)) - -To change file options such as chunk size, pass options to the constructor: - -.. code-block:: ruby - - file = File.open('/path/to/my-file.txt') - grid_file = Mongo::Grid::File.new( - file.read, - :filename => File.basename(file.path), - :chunk_size => 1024 - ) - -The following is a full list of the available options that files support. - -.. list-table:: - :header-rows: 1 - :widths: 40 80 - - * - Option - - Description - * - ``:chunk_size`` - - Sets the size of each file chunk in the database. - * - ``:content_type`` - - Set a content type for the file. - * - ``:filename`` (Required) - - The file name. - * - ``:upload_date`` - - The date the file was uploaded (stored). - - -Inserting Files -=============== - -Files can be inserted into the database one at a time. File chunks are inserted -by default into the ``fs.chunks`` collection and file metadata is inserted into the -``fs.files`` collection. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - file = Mongo::Grid::File.new('I am a file', :filename => 'new-file.txt') - - client.database.fs.insert_one(file) - -To insert into collections with a name prefix other than ``fs``, access the -filesystem with a ``:fs_name`` option. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - file = Mongo::Grid::File.new('I am a file', :filename => 'new-file.txt') - - client.database.fs(:fs_name => 'grid').insert_one(file) - -When the driver is inserting the first file into a bucket, it will attempt to create the required -indexes on ``files`` and ``chunks`` collections. The required indexes are as follows: - -.. code-block:: ruby - - # files collection - { :filename => 1, :uploadDate => 1 } - - # chunks collection - { :files_id => 1, :n => 1 }, { :unique => true } - -.. note:: - - If the indexes cannot be created, such as due to the current user lacking the permissions to do so, - the file insert will be aborted. If the application does not have permissions to create indexes, - a database administrator must create the required indexes ahead of time. - - If the bucket already has files, the driver will not attempt to create indexes, even if they are - missing and the current user has permissions to create them. In this case a database administrator - should create the needed indexes as soon as possible to ensure data integrity. - -Files can also be streamed as an alternative to a direct insert. - -.. code-block:: ruby - - client.database.fs.open_upload_stream(filename) do |stream| - stream.write(file) - end - -Finding Files -============= - -To retrieve a file from the database, call ``find_one`` with the appropriate filter. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - client.database.fs.find_one(:filename => 'new-file.txt') # Returns a Mongo::Grid::File - -Files can also be streamed as an alternative to a direct find. - -.. code-block:: ruby - - client.database.fs.open_download_stream(file_id) do |stream| - io.write(stream.read) - end - - fs.download_to_stream(file_id, io) - - -Deleting Files -============== - -To delete a file, pass the file object to ``delete_one``. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music') - fs = client.database.fs - file = fs.find_one(:filename => 'new-file.txt') - fs.delete_one(file) diff --git a/docs/reference/in-use-encryption.txt b/docs/reference/in-use-encryption.txt deleted file mode 100644 index 165f399b8b..0000000000 --- a/docs/reference/in-use-encryption.txt +++ /dev/null @@ -1,17 +0,0 @@ -.. _in-use-encryption: - -***************** -In-Use Encryption -***************** - -.. default-domain:: mongodb - -This section describes the different encryption methods in use by the Ruby -driver for MongoDB. - -.. toctree:: - :titlesonly: - - /reference/in-use-encryption/queryable-encryption - /reference/in-use-encryption/client-side-encryption - \ No newline at end of file diff --git a/docs/reference/in-use-encryption/client-side-encryption.txt b/docs/reference/in-use-encryption/client-side-encryption.txt deleted file mode 100644 index 78d1ca73c9..0000000000 --- a/docs/reference/in-use-encryption/client-side-encryption.txt +++ /dev/null @@ -1,900 +0,0 @@ -.. _client-side-encryption: - -********************** -Client-Side Encryption -********************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -New in MongoDB 4.2, client-side encryption allows administrators and developers -to encrypt specific fields in MongoDB documents before inserting them into the -database. - -With client-side encryption, developers can encrypt fields client-side without -any server-side configuration or directives. Client-side encryption supports -workloads where applications must guarantee that unauthorized parties, -including server administrators, cannot read the encrypted data. - -.. warning:: - - Enabling Client Side Encryption reduces the maximum write batch size and may - have a negative performance impact. - -Installation -============ - -Client-side encryption requires the installation of additional packages. - -libmongocrypt -~~~~~~~~~~~~~ - -Libmongocrypt is a C library used by the driver for client-side encryption. -To use client-side encryption, you must install the libmongocrypt library -on the machine running your Ruby program. - -The easiest way to install this library is to install `libmongocrypt-helper -`_ as follows: - -.. code-block:: bash - - gem install libmongocrypt-helper --pre - -The version number of libmongocrypt-helper is the version of included -libmongocrypt followed by the release number, e.g. 1.3.2.r1. -Because Ruby considers any letters in the version number to indicate a -pre-release version, the ``--pre`` flag is needed. - -The driver will automatically load libmongocrypt-helper - no further -configuration is needed. - -.. note:: - - libmongocrypt-helper currently only supports Linux operating systems. - -Alternatively you can download a pre-built binary distribution of libmongocrypt -and manually place the required shared object on your computer, as follows: - -- Download a tarball of all libmongocrypt variations `here `_. - -- Extract the file you downloaded. You will see a list of directories, each - corresponding to an operating system. Find the directory that matches your - operating system and open it. - -- Inside that folder, open the folder called "nocrypto." In either the - lib or lb64 folder, you will find the libmongocrypt.so or - libmongocrypt.dylib or libmongocrypt.dll file, depending on your OS. - -- Move that file to wherever you want to keep it on your machine. You may delete - the other files included in the tarball. - -To build the binary from source: - -- Follow the instructions in the README in the `libmongocrypt GitHub repo `_. - -Once you have the libmongocrypt binary on your machine, specify the path to the -binary using the LIBMONGOCRYPT_PATH environment variable. It is recommended that -you add this variable to your rc files. For example: - -.. code-block:: bash - - export LIBMONGOCRYPT_PATH=/path/to/your/libmongocrypt.so - -.. note:: - - The binary referenced in this section can be a pre-release version of - libmongocrypt which is not recommended for production environments. - -Automatic Encryption Shared Library -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The Automatic Encryption Shared Library is a dynamic library that enables your -client application to perform automatic encryption. It is only required for -automatic encryption, which is an enterprise-only feature. -If you only intend to use explicit encryption, you may skip this step. -The Automatic Encryption Shared Library provides the same functionality as -mongocryptd (see below), but does not require you to spawn another process -to perform automatic encryption. - -For installation instructions, see -`the MongoDB manual `_. - -When automatic encryption is enabled, libmongocrypt will look for the shared -library in the system library path, or try to load the library from a particular -place if ``:crypt_shared_lib_path`` option is provided when creating a client. -If the library can be loaded, then the driver will not try to spawn mongocryptd daemon. -The daemon will be still spawned if the shared library cannot be found. - -It is also possible to require using the shared library by passing -``crypt_shared_lib_required: true`` option when creating a client. In this case, -an error will be raised if the shared library cannot be loaded. - -.. note:: - All ``Mongo::Client`` objects in the same process should use the same setting - ``:crypt_shared_lib_path``, as it is an error to load more that one crypt_shared dynamic library simultaneously in a single operating system process. - -mongocryptd -~~~~~~~~~~~ - -Mongocryptd is an alternative to the Automatic Encryption Shared Library. -Mongocryptd is a daemon that tells the driver which fields to encrypt in a -given operation. It is only required for automatic encryption, which is an -enterprise-only feature. If you only intend to use explicit encryption, you may -skip this step. - -Mongocryptd comes pre-packaged with enterprise builds of the MongoDB server -(versions 4.2 and newer). For installation instructions, see the -`MongoDB manual `_. - -In order to configure mongocryptd (for example, which port it listens on or the -path used to spawn the daemon), it is necessary to pass different options to the -``Mongo::Client`` performing automatic encryption. See the :ref:`:extra_options ` -section of this tutorial for more information. - -Automatic Encryption -==================== - -Automatic encryption is a feature that allows users to configure a -``Mongo::Client`` instance to always encrypt specific document fields when -performing database operations. Once the ``Mongo::Client`` is configured, it -will automatically encrypt any field that requires encryption before writing -it to the database, and it will automatically decrypt those fields when reading -them. - -Client-side encryption implements envelope encryption, which is the practice of -encrypting data with a data key, which is in turn encrypted using a master key. -Thus, using client-side encryption with MongoDB involves three main steps: - -1. Create a master key -2. Create a data key (and encrypt it using the master key) -3. Encrypt data using the data key - -The example below demonstrates how to follow these steps with a local master key -in order to perform automatic encryption. - -.. note:: - - Automatic encryption is an enterprise only feature that only applies to - operations on a collection. Automatic encryption is not supported for operations - on a database or view, and operations that are not bypassed will result in - error (see `Auto Encryption Allow-List `_ - ). To bypass automatic encryption for all operations, set ``bypass_auto_encryption`` - to true in ``auto_encryption_options``. - -.. note:: - - Automatic encryption requires the authenticated user to have the listCollections privilege action. - -.. note:: - - When using Automatic Encryption, and a ``Mongo::Client`` instance that is configured - with ``:auto_encryption_options`` has a limited connection pool size - (i.e a non-zero ``:max_pool_size``, which is the default setting), a separate - internal ``Mongo::Client`` instance is created if any of the following are true: - - - ``auto_encryption_options[:key_vault_client]`` is not passed. - - ``auto_encryption_options[:bypass_automatic_encryption]`` is not passed or false. - - If an internal ``Mongo::Client`` instance is created, it is configured with - the same options as the parent client except ``:min_pool_size`` is set to 0 - and ``:auto_encryption_options`` is omitted. - -.. code-block:: ruby - - require 'mongo' - - ##################################### - # Step 1: Create a local master key # - ##################################### - - # A local master key is a 96-byte binary blob. - local_master_key = SecureRandom.random_bytes(96) - # => "\xB2\xBE\x8EN\xD4\x14\xC2\x13\xC3..." - - ############################# - # Step 2: Create a data key # - ############################# - - kms_providers = { - local: { - key: local_master_key - } - } - - # The key vault client is a Mongo::Client instance connected to the collection - # that will store your data keys. - key_vault_client = Mongo::Client.new(['localhost:27017']) - - # Use an instance of Mongo::ClientEncryption to create a new data key - client_encryption = Mongo::ClientEncryption.new( - key_vault_client, - key_vault_namespace: 'encryption.__keyVault', - kms_providers: kms_providers - ) - - data_key_id = client_encryption.create_data_key('local') - # => - - ####################################################### - # Step 3: Configure Mongo::Client for auto-encryption # - ####################################################### - - # Create a schema map, which tells the Mongo::Client which fields to encrypt - schema_map = { - 'encryption_db.encryption_coll': { - properties: { - encrypted_field: { - encrypt: { - keyId: [data_key_id], - bsonType: "string", - algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - bsonType: "object" - } - } - - # Configure the client for automatic encryption - client = Mongo::Client.new( - ['localhost:27017'], - auto_encryption_options: { - key_vault_namespace: 'encryption.__keyVault', - kms_providers: kms_providers, - schema_map: schema_map - }, - database: 'encryption_db', - ) - - collection = client['encryption_coll'] - collection.drop # Make sure there is no data in the collection - - # The string "sensitive data" will be encrypted and stored in the database - # as ciphertext - collection.insert_one(encrypted_field: 'sensitive data') - - # The data is decrypted before being returned to the user - collection.find(encrypted_field: 'sensitive data').first['encrypted_field'] - # => "sensitive data" - - # A client with no auto_encryption_options is unable to decrypt the data - client_no_encryption = Mongo::Client.new( - ['localhost:27017'], - database: 'encryption_db', - ) - client_no_encryption['encryption_coll'].find.first['encrypted_field'] - # => - -The example above demonstrates using automatic encryption with a local master key. -For more information about using other key management services to create a -master key and create data keys, see the following sections of this tutorial: - -- :ref:`Creating A Master Key ` -- :ref:`Creating A Data Key ` - -Explicit Encryption -=================== - -Explicit encryption is a feature that allows users to encrypt and decrypt -individual pieces of data such as strings, integers, or symbols. Explicit -encryption is a community feature and does not require an enterprise build -of the MongoDB server to use. To perform all explicit encryption and decryption -operations, use an instance of the ClientEncryption class. - -Client-side encryption implements envelope encryption, which is the practice of -encrypting data with a data key, which is in turn encrypted using a master key. -Thus, using client-side encryption with MongoDB involves three main steps: - -1. Create a master key -2. Create a data key (and encrypt it using the master key) -3. Encrypt data using the data key - -The example below demonstrates how to follow these steps with a local master key -in order to perform explicit encryption. - -.. code-block:: ruby - - require 'mongo' - - ##################################### - # Step 1: Create a local master key # - ##################################### - - # A local master key is a 96-byte binary blob. - local_master_key = SecureRandom.random_bytes(96) - # => "\xB2\xBE\x8EN\xD4\x14\xC2\x13\xC3..." - - ############################# - # Step 2: Create a data key # - ############################# - - kms_providers = { - local: { - key: local_master_key - } - } - - # The key vault client is a Mongo::Client instance connected to the collection - # that will store your data keys. - key_vault_client = Mongo::Client.new(['localhost:27017']) - - # Use an instance of Mongo::ClientEncryption to create a new data key - client_encryption = Mongo::ClientEncryption.new( - key_vault_client, - key_vault_namespace: 'encryption.__keyVault', - kms_providers: kms_providers - ) - - data_key_id = client_encryption.create_data_key('local') - # => - - ##################################################### - # Step 3: Encrypt a string with explicit encryption # - ##################################################### - - # The value to encrypt - value = 'sensitive data' - - # Encrypt the value - encrypted_value = client_encryption.encrypt( - 'sensitive data', - { - key_id: data_key_id, - algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - ) - - # Create the client you will use to read and write the data to MongoDB - client = Mongo::Client.new( - ['localhost:27017'], - database: 'encryption_db', - ) - collection = client['encryption_coll'] - collection.drop # Make sure there is no data in the collection - - # Insert the encrypted value into the collection - collection.insert_one(encrypted_field: encrypted_value) - - # Use the client to read the encrypted value from the database, then - # use the ClientEncryption object to decrypt it - find_result = collection.find(encrypted_field: encrypted_value).first['encrypted_field'] - # => (the find result is encrypted) - - unencrypted_result = client_encryption.decrypt(find_result) - # => "sensitive data" - -The example above demonstrates using explicit encryption with a local master key. -For more information about using other key management services to create a -master key and create data keys, see the following sections of this tutorial: - -- :ref:`Creating A Master Key ` -- :ref:`Creating A Data Key ` - -.. _creating-a-master-key: - -Creating a Master Key -===================== -Both automatic encryption and explicit encryption require an encryption master key. -This master key is used to encrypt data keys, which are in turn used to encrypt -user data. The master key can be generated in one of two ways: by creating a -local key, or by creating a key in a key management service. Currently -Ruby driver supports AWS Key Management Service (KMS), Azure Key Vault, and -Google Cloud Key Management (GCP KMS). - -.. _local-master-key: - -Local Master Key -~~~~~~~~~~~~~~~~ - -A local master key is a 96-byte binary string. It should be persisted -on your machine as an environment variable or in a text file. - -.. warning:: - - Using a local master key is insecure and not recommended if you plan - to use client-side encryption in production. - -Run the following code to generate a local master key using Ruby: - -.. code-block:: ruby - - local_master_key = SecureRandom.random_bytes(96) - # => "\xB2\xBE\x8EN\xD4\x14\xC2\x13\xC3..." (a binary blob) - -.. _remote-master-key: - -Remote Master Key -~~~~~~~~~~~~~~~~~ -It is recommended that you use a remote Key Management Service to create and -store your master key. To do so, follow steps of the -`"Set up a Remote Master Key" `_ -in the MongoDB Client-Side Encryption documentation. - -For more information about creating a master key, see the -`Create a Master Key `_ -section of the MongoDB manual. - -.. _creating-a-data-key: - -Creating a Data Key -=================== - -Once you have created a master key, create a data key by calling the -``#create_data_key`` method on an instance of the ``Mongo::ClientEncryption`` -class. This method generates a new data key and inserts it into the key vault -collection, which is the MongoDB collection in which you choose to store your -data keys. The ``#create_data_key`` method returns id of the newly-created -data key in the form of a BSON::Binary object. - -Create a Data Key Using a Local Master Key -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you have created a local master key, you may use it to generate a new data -key with the following code snippet: - -.. warning:: - - Using a local master key is insecure and not recommended if you plan - to use client-side encryption in production. - -.. code-block:: ruby - - # A Mongo::Client instance that will be used to connect to the key vault - # collection. Replace the server address with the address of the MongoDB - # server where you would like to store your key vault collection. - key_vault_client = Mongo::Client.new(['localhost:27017']) - - client_encryption = Mongo::ClientEncryption.new( - key_vault_client, - # Replace with the database and collection names for your key vault collection - key_vault_namespace: 'encryption.__keyVault', - kms_providers: { - local: { - key: local_master_key - } - } - ) - - data_key_id = client_encryption.create_data_key('local') - # => - -See the :ref:`Local Master Key ` section for more information -about generating a new local master key. - -Create a Data Key Using a Remote Master Key -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you have created an AWS KMS master key, note the access key ID and the secret access -key of the IAM user that has permissions to use the key. Additionally, note -the AWS region and the Amazon Resource Number (ARN) of your master key. You will -use that information to generate a data key. - -If you have created an Azure master key, note the tenant id, the client id, and -the client secret of the application that has permissions to use the key. -Additionally, note the key name, key version (if any), and key vault endpoint -for your master key. You will use that information to generate a data key. - -If you have created a GCP KMS master key, note the email and the private key, -and the client secret of the application that has permissions to use the key. -Additionally, note the project id, location, key ring, key name, and -key version (if any) for your master key. You will use that information to -generate a data key. - -Please note that GCP private key can be in different formats. Ruby driver -supports DER encoded RSA private key as base64 encoded string. For MRI Ruby -the driver additionally support PEM encoded RSA private key. - -If you have created a master key using a Key Management Interoperability -Protocol (KMIP) compatible key management server, note the server host and port, -and key id. You will use that information to generate a data key. You may also -need certificate authority certificate(s), as well as and your client -certificate and private key to authenticate to KMIP server. - -.. code-block:: ruby - - # A Mongo::Client instance that will be used to connect to the key vault - # collection. Replace the server address with the address of the MongoDB - # server where you would like to store your key vault collection. - key_vault_client = Mongo::Client.new(['localhost:27017']) - - client_encryption = Mongo::ClientEncryption.new( - key_vault_client, - # Replace with the database and collection names for your key vault collection - key_vault_namespace: 'encryption.__keyVault', - kms_providers: { - aws: { - access_key_id: 'IAM-ACCESS-KEY-ID', - secret_access_key: 'IAM-SECRET-ACCESS-KEY' - }, - azure: { - tenant_id: 'AZURE-TENANT-ID', - client_id: 'AZURE-CLIENT-ID', - client_secret: 'AZURE-CLIENT-SECRET' - }, - gcp: { - email: 'GCP-EMAIL', - # :private_key value should be GCP private key as base64 encoded - # DER RSA private key, or PEM RSA private key, if you are using MRI Ruby. - private_key: 'GCP-PRIVATE-KEY', - }, - kmip: { - # KMIP server endpoint may include port. - endpoint: 'KMIP-SERVER-HOST' - }, - # TLS options to connect to KMIP server. - kms_tls_options: { - kmip: { - ssl_ca_cert: 'PATH-TO-CA-FILE', - ssl_cert: 'PATH-TO-CLIENT-CERT-FILE', - ssl_key: 'PATH-TO-CLIENT-KEY-FILE' - } - } - } - ) - - aws_data_key_id = client_encryption.create_data_key( - 'aws', - { - master_key: { - region: 'REGION-OF-YOUR-MASTER-KEY', - key: 'ARN-OF-YOUR-MASTER-KEY' - } - } - ) - # => - - azure_data_key_id = client_encryption.create_data_key( - 'azure', - { - master_key: { - key_vault_endpoint: 'AZURE-KEY-VAULT-ENDPOINT', - key_name: 'AZURE-KEY-NAME' - } - } - ) - # => - - gcp_data_key_id = client_encryption.create_data_key( - 'gcp', - { - master_key: { - project_id: 'GCP-PROJECT-ID', - location: 'GCP-LOCATION', - key_ring: 'GCP-KEY-RING', - key_name: 'GCP-KEY-NAME', - } - } - ) - # => - -See the :ref:`Remote Master Key ` section of this tutorial -for more information about generating a new remote master key and finding the -information you need to create data keys. - -For more information about creating a data key, see the -`Create a Data Encryption Key `_ -section of the MongoDB manual. - -For a list of possible KMS TLS options -see :manual:`create client reference `. -``Mongo::ClientEncryption`` constructor accepts same ``ssl_`` options as -``Mongo::Client``. - -Auto-Encryption Options -======================= - -Automatic encryption can be configured on a ``Mongo::Client`` using the -``auto_encryption_options`` option ``Hash``. This section provides an overview -of the fields inside ``auto_encryption_options`` and explains how to choose their -values. - -``:key_vault_client`` -~~~~~~~~~~~~~~~~~~~~~ - -The key vault client is a ``Mongo::Client`` instance that will be used to connect -to the MongoDB collection containing your encryption data keys. For example, if -your key vault was hosted on a MongoDB instance at ``localhost:30000``: - -.. code-block:: ruby - - key_vault_client = Mongo::Client.new(['localhost:30000']) - - Mongo::Client.new(['localhost:27017], - auto_encryption_options: { - key_vault_client: key_vault_client, - # ... (Fill in other options here) - } - ) - -If your data keys are stored in the same MongoDB instance that stores your encrypted -data, you may leave this option blank, and the top-level client will be used -to insert and fetch data keys. - -``:key_vault_namespace`` -~~~~~~~~~~~~~~~~~~~~~~~~ - -The key vault namespace is a ``String`` in the format ``"database_name.collection_name"``, -where ``database_name`` and ``collection_name`` are the name of the database and -collection in which you would like to store your data keys. For example, if your data -keys are stored in the ``encryption`` database in the ``__keyVault`` collection: - -.. code-block:: ruby - - Mongo::Client.new(['localhost:27017], - auto_encryption_options: { - key_vault_namespace: 'encryption.__keyVault', - # ... (Fill in other options here) - } - ) - -There is no default key vault namespace, and this option must be provided. - -``:kms_providers`` -~~~~~~~~~~~~~~~~~~ - -A Hash that contains KMS provider names as keys, and provider options as values. - -.. code-block:: ruby - - Mongo::Client.new(['localhost:27017], - auto_encryption_options: { - key_vault_namespace: 'encryption.__keyVault', - kms_providers: { - aws: { - access_key_id: 'IAM-ACCESS-KEY-ID', - secret_access_key: 'IAM-SECRET-ACCESS-KEY' - }, - azure: { - tenant_id: 'AZURE-TENANT-ID', - client_id: 'AZURE-CLIENT-ID', - client_secret: 'AZURE-CLIENT-SECRET' - }, - gcp: { - email: 'GCP-EMAIL', - # :private_key value should be GCP private key as base64 encoded - # DER RSA private key, or PEM RSA private key, if you are using MRI Ruby. - private_key: 'GCP-PRIVATE-KEY', - }, - kmip: { - # KMIP server endpoint may include port. - endpoint: 'KMIP-SERVER-HOST' - }, - # TLS options to connect to KMIP server. - kms_tls_options: { - kmip: { - ssl_ca_cert: 'PATH-TO-CA-FILE', - ssl_cert: 'PATH-TO-CLIENT-CERT-FILE', - ssl_key: 'PATH-TO-CLIENT-KEY-FILE' - } - } - } - } - ) - -The client can retrieve AWS credentials from the environment or from EC2 or ECS -metadata endpoints. To retrieve credentials automatically, specify an empty Hash -as KMS provider options for AWS: - -.. code-block:: ruby - - Mongo::Client.new(['localhost:27017'], - auto_encryption_options: { - key_vault_namespace: 'encryption.__keyVault', - kms_providers: { - aws: {} - } - } - ) - -See :ref:`"Automatically Retrieving Credentials" ` -for more detailed information about the credential retrieval. - -The client can retrieve GCP credentials from the Google Compute Engine -metadata endpoints. To retrieve credentials automatically, specify an empty Hash -as KMS provider options for GCP: - -.. code-block:: ruby - - Mongo::Client.new(['localhost:27017'], - auto_encryption_options: { - key_vault_namespace: 'encryption.__keyVault', - kms_providers: { - gcp: {} - } - } - ) - -``:kms_tls_options`` -~~~~~~~~~~~~~~~~~~~~ - -A Hash that contains KMP provider names as keys, and TLS options to connect to -corresponding providers. - -.. code-block:: ruby - - Mongo::Client.new(['localhost:27017], - auto_encryption_options: { - key_vault_namespace: 'encryption.__keyVault', - kms_providers: { - kmip: { - endpoint: 'KMIP-SERVER-HOST' - } - }, - kms_tls_options: { - kmip: { - ssl_ca_cert: 'PATH-TO-CA-FILE', - ssl_cert: 'PATH-TO-CLIENT-CERT-FILE', - ssl_key: 'PATH-TO-CLIENT-KEY-FILE' - } - } - } - ) - - -``:schema_map`` -~~~~~~~~~~~~~~~ - -A schema map is a Hash with information about which fields to automatically -encrypt and decrypt. - -The code snippet at the top of this tutorial demonstrates creating a schema -map using a Ruby ``Hash``. While this will work, schema maps can grow quite -large and it could be unweildy to include them in your Ruby code. Instead, it is -recommended that you store them in a separate JSON (JavaScript Object Notation) -file. - -Before creating the JSON file, Base64-encode the UUID of the your data key. - -.. code-block:: ruby - - Base64.encode64(data_key_id.data) - # => "sr6OTtQUwhPD..." (a base64-encoded string) - -Then, create a new JSON file containing your schema map in the format defined by -the JSON Schema Draft 4 standard syntax. You can read more about formatting -your schema map in the :manual:`Automatic Encryption Rules` -section of the MongoDB manual. - -.. code-block:: json - - { - "encryption_db.encryption_coll": { - "properties": { - "encrypted_field": { - "encrypt": { - "keyId": [{ - "$binary": { - "base64": "YOUR-BASE64-ENCODED-DATA-KEY-ID", - "subType": "04" - } - }], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - } - -When you intend to use your schema map, convert it to a Ruby ``Hash`` using the -``BSON::ExtJSON`` module in the ``bson`` Ruby gem. - -.. code-block:: ruby - - schema_map = BSON::ExtJSON.parse(File.read('/path/to/your/file.json')) - # => { 'encryption_db.encryption_coll' => { ... } } - - Mongo::Client.new(['localhost:27017], - auto_encryption_options: { - schema_map: schema_map, - # ... (Fill in other options here) - } - ) - -.. note:: - - It is also possible to supply a schema map as a validator on a MongoDB collection. - This is referred to as a "remote schema map," while providing the schema map as - an option on the ``Mongo::Client`` is called a "local schema map." - - Supplying a local schema map provides more security than relying on JSON schemas - obtained from the server. It protects against a malicious server advertising - a false JSON schema, which could trick the client into sending unencrypted - data that should be encrypted. - - See :manual:`Server-Side Field Level Encryption Enforcement` - in the MongoDB manual for more information about using the schema map to - create a JSON schema validator on your collection. - -.. seealso:: - - `Specify Encrypted Fields Using JSON Schema `_, - :manual:`Automatic Encryption Rules` - -.. _schema-map-path: - -``:schema_map_path`` -~~~~~~~~~~~~~~~~~~~~ - -It is also possible to load schema map from a file. Prepare the schema map as -described above, save it to file, and then pass path to the file using -``:schema_map_path`` option. - -.. code-block:: ruby - - Mongo::Client.new(['localhost:27017], - auto_encryption_options: { - schema_map_path: '/path/to/your/file.json', - # ... (Fill in other options here) - } - ) - -``:bypass_auto_encryption`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``:bypass_auto_encryption`` option is a ``Boolean`` that specifies whether the -``Mongo::Client`` should skip encryption when writing to the database. If -``:bypass_auto_encryption`` is ``true``, the client will still perform automatic -decryption of any previously-encrypted data. - -.. code-block:: ruby - - Mongo::Client.new(['localhost:27017], - auto_encryption_options: { - bypass_auto_encryption: true, - # ... (Fill in other options here) - } - ) - -.. _cse-extra-options: - -``:extra_options`` -~~~~~~~~~~~~~~~~~~ - -``:extra_options`` is a ``Hash`` of options related to spawning mongocryptd. -Every option in this ``Hash`` has a default value, so it is only necessary to -provide the options whose defaults you want to override. - -- ``:mongocryptd_spawn_args`` - This is an ``Array`` containing arguments - for spawning mongocryptd. The Ruby driver will pass these arguments to - mongocryptd on spawning the daemon. Possible arguments are: - - - ``"--idleShutdownTimeoutSecs"`` - The number of seconds mongocryptd must remain - idle before it shuts itself down. The default value is 60. - - ``"--port"`` - The port at which mongocryptd will listen for connections. The - default is 27020. - -- ``:mongocryptd_uri`` - The URI that the driver will use to connect to mongocryptd. - By default, this is ``"mongodb://localhost:27020"``. - -- ``:mongocryptd_spawn_path`` - The path to the mongocryptd executable. The default - is ``"mongocryptd"``. - -- ``:mongocryptd_bypass_spawn`` - A ``Boolean`` indicating whether the driver should - skip spawning mongocryptd. - -For example, if you would like to run mongocryptd on port 30000, provide -``extra_options`` as follows: - -.. code-block:: ruby - - Mongo::Client.new(['localhost:27017], - auto_encryption_options: { - extra_options: { - mongocryptd_spawn_args: ['--port=30000'], - mongocryptd_uri: 'mongodb://localhost:30000', - } - # ... (Fill in other options here) - } - ) - -.. warning:: - - The contents of ``:extra_options`` is subject to change in future versions - of the client-side encryption API. diff --git a/docs/reference/in-use-encryption/queryable-encryption.txt b/docs/reference/in-use-encryption/queryable-encryption.txt deleted file mode 100644 index 2be80ed5f2..0000000000 --- a/docs/reference/in-use-encryption/queryable-encryption.txt +++ /dev/null @@ -1,236 +0,0 @@ -.. _queryable-encryption: - -********************** -Queryable Encryption -********************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Queryable encryption is a new feature in MongoDB 6.0. It also requires -libmongocrypt version 1.5.2 or above. - -You can find more information about queryable encryption in `MongoDB Manual -`_. - -.. note:: - - The queryable encryption feature is in public technical preview. - Therefore, the following options should be considered experimental - and are subject to change: - - - ``:encrypted_fields_map`` and ``:bypass_query_analysis`` in auto encryption options. - - ``:contention_factor`` and ``:query_type`` in client encryption options. - -The following examples assume you are familiar with the concepts and techniques -described in :ref:`Client-Side Encryption `. - -Below is an example of using automatic queryable encryption using the Ruby driver: - -.. code-block:: ruby - - require 'mongo' - - ##################################### - # Step 1: Create a local master key # - ##################################### - - # A local master key is a 96-byte binary blob. - local_master_key = SecureRandom.random_bytes(96) - # => "\xB2\xBE\x8EN\xD4\x14\xC2\x13\xC3..." - - ############################# - # Step 2: Create a data key # - ############################# - - kms_providers = { - local: { - key: local_master_key - } - } - - # The key vault client is a Mongo::Client instance - # that will be used to store your data keys. - key_vault_client = Mongo::Client.new('mongodb://localhost:27017,localhost:27018') - - # Use an instance of Mongo::ClientEncryption to create a new data key - client_encryption = Mongo::ClientEncryption.new( - key_vault_client, - key_vault_namespace: 'encryption.__keyVault', - kms_providers: kms_providers - ) - - data_key_id = client_encryption.create_data_key('local') - # => - - ####################################################### - # Step 3: Configure Mongo::Client for auto-encryption # - ####################################################### - - # Create an encrypted fields map, which tells the Mongo::Client which fields to encrypt. - encrypted_fields_map = { - 'encryption_db.encryption_coll' => { - fields: [ - { - path: 'encrypted_field', - bsonType: 'string', - keyId: data_key_id, - queries: { - queryType: 'equality' - } - } - ] - } - } - - # Configure the client for automatic encryption - client = Mongo::Client.new( - 'mongodb://localhost:27017,localhost:27018', - auto_encryption_options: { - key_vault_namespace: 'encryption.__keyVault', - kms_providers: kms_providers, - encrypted_fields_map: encrypted_fields_map, - }, - database: 'encryption_db' - ) - - # Make sure there is no data in the collection. - client.database.drop - - # Create encrypted collection explicitly. - collection = client['encryption_coll'].create - - # The string "sensitive data" will be encrypted and stored in the database - # as ciphertext - collection.insert_one(encrypted_field: 'sensitive data') - - # The data is decrypted before being returned to the user - collection.find(encrypted_field: 'sensitive data').first['encrypted_field'] - # => "sensitive data" - - # A client with no auto_encryption_options is unable to decrypt the data - client_no_encryption = Mongo::Client.new(['localhost:27017'], database: 'encryption_db') - client_no_encryption['encryption_coll'].find.first['encrypted_field'] - # => - -The example above demonstrates using automatic encryption with a local master key. -For more information about using other key management services to create a -master key and create data keys, see the following sections of the :ref:`Client-Side Encryption ` tutorial: - -- :ref:`Creating A Master Key ` -- :ref:`Creating A Data Key ` - -Below is an example of explicit queryable encryption. - -.. code-block:: ruby - - require 'mongo' - - ##################################### - # Step 1: Create a local master key # - ##################################### - - # A local master key is a 96-byte binary blob. - local_master_key = SecureRandom.random_bytes(96) - # => "\xB2\xBE\x8EN\xD4\x14\xC2\x13\xC3..." - - ############################# - # Step 2: Create a data key # - ############################# - - kms_providers = { - local: { - key: local_master_key - } - } - - # The key vault client is a Mongo::Client instance - # that will be used to store your data keys. - key_vault_client = Mongo::Client.new('mongodb://localhost:27017,localhost:27018') - - # Use an instance of Mongo::ClientEncryption to create a new data key - client_encryption = Mongo::ClientEncryption.new( - key_vault_client, - key_vault_namespace: 'encryption.__keyVault', - kms_providers: kms_providers - ) - - data_key_id = client_encryption.create_data_key('local') - # => - - ########################################## - # Step 3: Create an encrypted collection # - ########################################## - encrypted_fields = { - fields: [ - { - path: 'encrypted_field', - bsonType: 'string', - keyId: data_key_id, - queries: { - queryType: 'equality', - contention: 0 - } - } - ] - } - - # Create the client you will use to read and write the data to MongoDB - # Please note that to insert or query with an "Indexed" encrypted payload, - # you should use a ``Mongo::Client`` that is configured with ``:auto_encryption_options``. - # ``auto_encryption_options[:bypass_query_analysis]`` may be true. - # ``auto_encryption_options[:bypass_auto_encryption]`` must be not set or false. - client = Mongo::Client.new( - ['localhost:27017'], - auto_encryption_options: { - key_vault_namespace: 'encryption.__keyVault', - kms_providers: kms_providers, - bypass_query_analysis: true, - }, - database: 'encryption_db', - ) - - # Make sure there is no data in the collection. - client['encryption_coll'].drop(encrypted_fields: encrypted_fields) - # Create encrypted collection explicitly. - client['encryption_coll'].create(encrypted_fields: encrypted_fields) - - ##################################################### - # Step 4: Encrypt a string with explicit encryption # - ##################################################### - - # The value to encrypt - value = 'sensitive data' - - # Encrypt the value - insert_payload = client_encryption.encrypt( - 'sensitive data', - { - key_id: data_key_id, - algorithm: "Indexed", - contention_factor: 0 - } - ) - - # Insert the encrypted value into the collection - client['encryption_coll'].insert_one(encrypted_field: insert_payload) - - # Use the client to read the encrypted value from the database, then - # use the ClientEncryption object to decrypt it. - find_payload = client_encryption.encrypt( - 'sensitive data', - { - key_id: data_key_id, - algorithm: "Indexed", - contention_factor: 0, - query_type: "equality" - } - ) - - find_result = client['encryption_coll'].find(encrypted_field: find_payload).first['encrypted_field'] - # => 'sensitive data' diff --git a/docs/reference/indexing.txt b/docs/reference/indexing.txt deleted file mode 100644 index a536c8870f..0000000000 --- a/docs/reference/indexing.txt +++ /dev/null @@ -1,172 +0,0 @@ -******** -Indexing -******** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -The driver provides the ability to create, drop and view -:manual:`indexes` on a collection through the ``indexes`` attribute: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music') - client[:bands].indexes - # => #, @batch_size=nil, @options={}> - - -Creating Indexes -================ - -Indexes can be created one at a time, or several can be created in a single -operation. When creating multiple indexes on MongoDB 3.0 and later, the indexes -are created in parallel; on earlier versions they are created sequentially. - -To create a single index, use ``indexes#create_one``, passing the key -specification as the first argument and options as the second argument: - -.. code-block:: ruby - - client[:bands].indexes.create_one(genre: 1) - - client[:bands].indexes.create_one( - { name: 1 }, - unique: true, expire_after: 120, - ) - -To create multiple indexes, use ``indexes#create_many`` which accepts an array -of index specifications. Unlike ``create_one``, each index specification -is a hash with the ``key`` key mapped to the key specification and the -options being specified on the top level. - -.. code-block:: ruby - - client[:bands].indexes.create_many([ - { key: { genre: 1 } }, - { key: { name: 1 }, unique: true, expire_after: 120 }, - ]) - -.. _index-options: - -The following is a full list of the available options that can be added -when creating indexes. These options mirror the options supported by the -:manual:`createIndex command`. - -.. list-table:: - :header-rows: 1 - :widths: 40 80 - - * - Option - - Description - * - ``:background`` - - Either ``true`` or ``false``. Tells the index to be created in the background. - * - ``:expire_after`` - - Number of seconds to expire documents in the collection after. - * - ``:name`` - - The name of the index. - * - ``:sparse`` - - Whether the index should be sparse or not, either ``true`` or ``false``. - * - ``:storage_engine`` - - The name of the storage engine for this particular index. - * - ``:version`` - - The index format version to use. - * - ``:default_language`` - - The default language of text indexes. - * - ``:language_override`` - - The field name to use when overriding the default language. - * - ``:text_version`` - - The version format for text index storage. - * - ``:weights`` - - A document specifying fields and weights in text search. - * - ``:sphere_version`` - - The 2d sphere index version. - * - ``:bits`` - - Sets the maximum boundary for latitude and longitude in the 2d index. - * - ``:max`` - - Maximum boundary for latitude and longitude in the 2d index. - * - ``:min`` - - Minimum boundary for latitude and longitude in the 2d index. - * - ``:bucket_size`` - - The number of units within which to group the location values in a geo haystack index. - * - ``:partial_filter_expression`` - - A filter for a partial index. - * - ``:hidden`` - - A Boolean specifying whether the index should be hidden; a hidden index - is one that exists on the collection but will not be used by the query planner. - -The :commit_quorum option -------------------------- -On MongoDB server versions 4.4 and newer, the ``:commit_quorum`` option may be -specified on index creation. This option differs from other index options in that -it determines server behavior during index creation, rather than determining -the behavior of an individual index. - -The ``:commit_quorum`` option specifies how many voting, data-bearing members -of a replica set must complete the index build before the index is ready. -Possible values are integers (0 to the number of voting, data-bearing members -of the replica set), "majority", or "votingMembers". - -To specify ``:commit_quorum`` when creating one index, add another option -to the second argument of the ``indexes#create_one`` method: - -.. code-block:: ruby - - client[:bands].indexes.create_one( - { name: 1 }, - unique: true, expire_after: 120, commit_quorum: 'majority' - ) - -To specify create options when creating multiple indexes, add a Hash specifying -``:commit_quorum`` as a final element to the Array of indexes passed to -``indexes#create_many``. Note that this Hash MUST be the final element in the -Array. - -.. code-block:: ruby - - client[:bands].indexes.create_many([ - { key: { genre: 1 } }, - { key: { name: 1 }, unique: true, expire_after: 120 }, - { commit_quorum: 'majority' }, - ]) - -Dropping Indexes -================ - -To drop an index, call ``indexes#drop_one`` or ``indexes#drop_all``. - -.. code-block:: ruby - - # Drops the name_1 index. - client[:bands].indexes.drop_one( 'name_1' ) - - # Drops all indexes in the collection. - client[:bands].indexes.drop_all - - -Listing Indexes -=============== - -To list the indexes, iterate the ``indexes`` object: - -.. code-block:: ruby - - client[:bands].indexes.each do |index_spec| - p index_spec - # {"v"=>2, "key"=>{"_id"=>1}, "name"=>"_id_"} - # {"v"=>2, "key"=>{"genre"=>1}, "name"=>"genre_1"} - # {"v"=>2, "unique"=>true, "key"=>{"name"=>1}, "name"=>"name_1", - # "expireAfterSeconds"=>120} - end - -Each iteration returns an index specification as returned by the -:manual:`listIndexes` command. - -.. note:: - - The shape and contents of the index specifications returned by this method - may change from one version of MongoDB to another. diff --git a/docs/reference/map-reduce.txt b/docs/reference/map-reduce.txt deleted file mode 100644 index f01b64b556..0000000000 --- a/docs/reference/map-reduce.txt +++ /dev/null @@ -1,132 +0,0 @@ -********** -Map-Reduce -********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -:manual:`Map-Reduce ` is a data processing paradigm for -condensing large volumes of data into aggregated results. - -.. note:: - - The map-reduce operation is deprecated. - The :ref:`aggregation framework ` provides better performance - and usability than map-reduce operations, and should be preferred for - new development. - -A map-reduce operation is issued on a collection view, as obtained from -``Collection#find`` method, by calling the ``map_reduce`` method on the -view. The ``map_reduce`` method takes three arguments: the mapper, the -reducer and map-reduce options. The mapper and the reducer must be provided -as strings containing JavaScript functions. - -For example, given the following collection with values 1 through 10: - -.. code-block:: ruby - - coll = client['foo'] - 10.times do |i| - coll.insert_one(v: i) - end - -The following invocation will sum up the values less than 6: - -.. code-block:: ruby - - coll.find(v: {'$lt' => 6}).map_reduce( - 'function() { emit(null, this.v) }', - 'function(key, values) { return Array.sum(values) }', - ).first['value'] - # => 15.0 - -The ``map_reduce`` method returns an instance of -``Mongo::Collection::View::MapReduce`` - a map-reduce view which holds -the parameters to be used for the operation. To execute the operation, either -iterate the results (by using e.g. ``each``, ``first`` or ``to_a`` on the -view object) or invoke the ``execute`` method. The ``execute`` method issues -the map-reduce operation but does not return the result set from the server, -and is primarily useful for when the output of the operation is directed to -a collection as follows: - -.. code-block:: ruby - - coll.find(...).map_reduce(...).out('destination_collection').execute - -Note that: - -- If the results of map-reduce are not directed to a collection, they are - said to be retrieved inline. In this case the entire result set must fit in - the 16 MiB BSON document size limit. -- If the results of map-reduce are directed to a collection, and the - map-reduce view is iterated, the driver automatically retrieves the - entire collection and returns its contents as the result set. The - collection is retrieved without sorting. If map-reduce is performed into - a collection that is not empty, the driver will return the documents - as they exist in the collection after the map-reduce operation completes, - which may include the documents that were in the collection prior to the - map-reduce operation. - -.. code-block:: ruby - - coll.find(...).map_reduce(...).out('destination_collection').each do |doc| - # ... - end - - coll.find(...).map_reduce(...).out(replace: 'destination_collection', db: 'db_name').each do |doc| - # ... - end - -Given a map-reduce view, it can be configured using the following methods: - -.. list-table:: - :header-rows: 1 - :widths: 20 80 - - * - Method - - Description - - * - ``js_mode`` - - Sets the ``jsMode`` flag for the operation. - - * - ``out`` - - Directs the output to the specified collection, instead of returning - the result set. - - * - ``scope`` - - Sets the scope for the operation. - - * - ``verbose`` - - Sets whether to include the timing information in the result. - -The following accessor methods are defined on the view object: - -.. list-table:: - :header-rows: 1 - :widths: 20 80 - - * - Method - - Description - - * - ``js_mode`` - - Returns the current ``jsMode`` flag value. - - * - ``map_function`` - - Returns the map function as a string. - - * - ``out`` - - Returns the current output location for the operation. - - * - ``reduce_function`` - - Returns the reduce function as a string. - - * - ``scope`` - - Returns the current scope for the operation. - - * - ``verbose`` - - Returns whether to include the timing information in the result. diff --git a/docs/reference/monitoring.txt b/docs/reference/monitoring.txt deleted file mode 100644 index 06328456a4..0000000000 --- a/docs/reference/monitoring.txt +++ /dev/null @@ -1,469 +0,0 @@ -********** -Monitoring -********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -The driver allows the application to be notified when certain events happen. -These events are organized into the following categories: - -- Command monitoring -- Topology lifecycle -- Server lifecycle -- Server heartbeats -- Connection pools and connections - -Topology and server events are part of Server Discovery and Monitoring (SDAM). - - -.. _command-monitoring: - -Command Monitoring -================== - -All user-initiated commands that are sent to the server publish events that -can be subscribed to for fine grained information. The monitoring API -publishes a guaranteed start event for each command, then either a succeeded -or a failed event. A subscriber must implement 3 methods: ``started``, -``succeeded``, and ``failed``, each which takes a single parameter for -the event. The following is an example logging subscriber based on a -logging subscriber used internally by the driver: - -.. code-block:: ruby - - class CommandLogSubscriber - include Mongo::Loggable - - def started(event) - # The default inspection of a command which is a BSON document gets - # truncated in the middle. To get the full rendering of the command, the - # ``to_json`` method can be called on the document. - log_debug("#{prefix(event)} | STARTED | #{format_command(event.command.to_json)}") - end - - def succeeded(event) - log_debug("#{prefix(event)} | SUCCEEDED | #{event.duration}s") - end - - def failed(event) - log_debug("#{prefix(event)} | FAILED | #{event.message} | #{event.duration}s") - end - - private - - def logger - Mongo::Logger.logger - end - - def format_command(args) - begin - args.inspect - rescue Exception - '' - end - end - - def format_message(message) - format("COMMAND | %s".freeze, message) - end - - def prefix(event) - "#{event.address.to_s} | #{event.database_name}.#{event.command_name}" - end - end - -To register a custom subscriber, you can do so globally for -all clients or on a per-client basis: - -.. code-block:: ruby - - subscriber = CommandLogSubscriber.new - - Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::COMMAND, subscriber) - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test' ) - client.subscribe( Mongo::Monitoring::COMMAND, subscriber ) - -Sample output: - -.. code-block:: none - - D, [2018-09-23T13:47:31.258020 #4692] DEBUG -- : COMMAND | 127.0.0.1:27027 | test.hello | STARTED | {"hello"=>1, "$readPreference"=>{"mode"=>"primary"}, "lsid"=>{"id"=>}} - D, [2018-09-23T13:47:31.259145 #4692] DEBUG -- : COMMAND | 127.0.0.1:27027 | test.hello | SUCCEEDED | 0.000791175s - - -.. _sdam: - -Server Discovery And Monitoring -=============================== - -The Ruby driver implements `Server Discovery And Monitoring (SDAM) specification -`_. -and makes the following events available to the application: - -- Topology opening -- Server opening -- Server description changed -- Topology changed -- Server closed -- Topology closed -- Heartbeat events (covered below in a separate section) - -For all events other than the heartbeat events, the ``succeeded`` method -will be called on each event subscriber with the event as the sole argument. -Available data for events varies, therefore to log the events a separate -class is needed for each event type. A simple SDAM logging subscriber -can look like the following: - -.. code-block:: ruby - - class SDAMLogSubscriber - include Mongo::Loggable - - def succeeded(event) - log_debug(format_event(event)) - end - - private - - def logger - Mongo::Logger.logger - end - - def format_message(message) - format("SDAM | %s".freeze, message) - end - end - - class TopologyOpeningLogSubscriber < SDAMLogSubscriber - private - - def format_event(event) - "Topology type '#{event.topology.display_name}' initializing." - end - end - - class ServerOpeningLogSubscriber < SDAMLogSubscriber - private - - def format_event(event) - "Server #{event.address} initializing." - end - end - - class ServerDescriptionChangedLogSubscriber < SDAMLogSubscriber - private - - def format_event(event) - "Server description for #{event.address} changed from " + - "'#{event.previous_description.server_type}' to '#{event.new_description.server_type}'." - end - end - - class TopologyChangedLogSubscriber < SDAMLogSubscriber - private - - def format_event(event) - if event.previous_topology != event.new_topology - "Topology type '#{event.previous_topology.display_name}' changed to " + - "type '#{event.new_topology.display_name}'." - else - "There was a change in the members of the '#{event.new_topology.display_name}' " + - "topology." - end - end - end - - class ServerClosedLogSubscriber < SDAMLogSubscriber - private - - def format_event(event) - "Server #{event.address} connection closed." - end - end - - class TopologyClosedLogSubscriber < SDAMLogSubscriber - private - - def format_event(event) - "Topology type '#{event.topology.display_name}' closed." - end - end - -To subscribe to SDAM events globally: - -.. code-block:: ruby - - topology_opening_subscriber = TopologyOpeningLogSubscriber.new - server_opening_subscriber = ServerOpeningLogSubscriber.new - server_description_changed_subscriber = ServerDescriptionChangedLogSubscriber.new - topology_changed_subscriber = TopologyChangedLogSubscriber.new - server_closed_subscriber = ServerClosedLogSubscriber.new - topology_closed_subscriber = TopologyClosedLogSubscriber.new - - Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::TOPOLOGY_OPENING, - topology_opening_subscriber) - Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::SERVER_OPENING, - server_opening_subscriber) - Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::SERVER_DESCRIPTION_CHANGED, - server_description_changed_subscriber) - Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::TOPOLOGY_CHANGED, - topology_changed_subscriber) - Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::SERVER_CLOSED, - server_closed_subscriber) - Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::TOPOLOGY_CLOSED, - topology_closed_subscriber) - -Subscribing to SDAM events for a single client is a little more involved -since the events may be published during the client's construction: - -.. code-block:: ruby - - topology_opening_subscriber = TopologyOpeningLogSubscriber.new - server_opening_subscriber = ServerOpeningLogSubscriber.new - server_description_changed_subscriber = ServerDescriptionChangedLogSubscriber.new - topology_changed_subscriber = TopologyChangedLogSubscriber.new - server_closed_subscriber = ServerClosedLogSubscriber.new - topology_closed_subscriber = TopologyClosedLogSubscriber.new - - sdam_proc = Proc.new do |client| - client.subscribe(Mongo::Monitoring::TOPOLOGY_OPENING, - topology_opening_subscriber) - client.subscribe(Mongo::Monitoring::SERVER_OPENING, - server_opening_subscriber) - client.subscribe(Mongo::Monitoring::SERVER_DESCRIPTION_CHANGED, - server_description_changed_subscriber) - client.subscribe(Mongo::Monitoring::TOPOLOGY_CHANGED, - topology_changed_subscriber) - client.subscribe(Mongo::Monitoring::SERVER_CLOSED, - server_closed_subscriber) - client.subscribe(Mongo::Monitoring::TOPOLOGY_CLOSED, - topology_closed_subscriber) - end - - client = Mongo::Client.new(['127.0.0.1:27017'], database: 'test', - sdam_proc: sdam_proc) - -Sample output: - -.. code-block:: none - - D, [2018-10-09T13:58:03.489461 #22079] DEBUG -- : SDAM | Topology type 'Unknown' initializing. - D, [2018-10-09T13:58:03.489699 #22079] DEBUG -- : SDAM | Server 127.0.0.1:27100 initializing. - D, [2018-10-09T13:58:03.491384 #22079] DEBUG -- : SDAM | Server description for 127.0.0.1:27100 changed from 'unknown' to 'unknown'. - D, [2018-10-09T13:58:03.491642 #22079] DEBUG -- : SDAM | Server localhost:27100 initializing. - D, [2018-10-09T13:58:03.493199 #22079] DEBUG -- : SDAM | Server description for localhost:27100 changed from 'unknown' to 'primary'. - D, [2018-10-09T13:58:03.493473 #22079] DEBUG -- : SDAM | Server localhost:27101 initializing. - D, [2018-10-09T13:58:03.494874 #22079] DEBUG -- : SDAM | Server description for localhost:27101 changed from 'unknown' to 'secondary'. - D, [2018-10-09T13:58:03.495139 #22079] DEBUG -- : SDAM | Server localhost:27102 initializing. - D, [2018-10-09T13:58:03.496504 #22079] DEBUG -- : SDAM | Server description for localhost:27102 changed from 'unknown' to 'secondary'. - D, [2018-10-09T13:58:03.496777 #22079] DEBUG -- : SDAM | Topology type 'Unknown' changed to type 'ReplicaSetNoPrimary'. - D, [2018-10-09T13:58:03.497306 #22079] DEBUG -- : SDAM | Server 127.0.0.1:27100 connection closed. - D, [2018-10-09T13:58:03.497606 #22079] DEBUG -- : SDAM | Topology type 'ReplicaSetNoPrimary' changed to type 'ReplicaSetWithPrimary'. - - # client.close - - D, [2018-10-09T13:58:05.342057 #22079] DEBUG -- : SDAM | Server localhost:27100 connection closed. - D, [2018-10-09T13:58:05.342299 #22079] DEBUG -- : SDAM | Server localhost:27101 connection closed. - D, [2018-10-09T13:58:05.342565 #22079] DEBUG -- : SDAM | Server localhost:27102 connection closed. - D, [2018-10-09T13:58:05.342693 #22079] DEBUG -- : SDAM | Topology type 'ReplicaSetWithPrimary' closed. - -.. note:: - - ``:sdam_proc`` client option applies only to the client during whose - construction it is given. When certain client options are changed via the - ``Client#with`` call, a new cluster may be created by the driver with - a default set of event subscribers. If this happens, the provided - ``:sdam_proc`` is not called and the application may miss events. - - -.. _server-heartbeats: - -Server Heartbeats -================= - -The application can be notified of each server heartbeat by subscribing -to SERVER_HEARTBEAT topic. A server heartbeat listener must implement -three methods: ``started``, ``succeeded`` and ``failed``. Each heartbeat -invokes the ``started`` method on the listener, and then either ``succeeded`` -or ``failed`` method depending on the outcome of the heartbeat. - -All heartbeat events contain the address of the server that the heartbeat -was sent to. Succeeded and failed events contain the round trip time for -the hello or legacy hello command. Failed event also contains the exception -instance that was raised during hello or legacy hello command execution. -Please review the API documentation for ServerHeartbeatStarted, -ServerHeartbeatSucceeded and ServerHeartbeatFailed for event attribute details. - -The following is an example logging heartbeat event subscriber: - -.. code-block:: ruby - - class HeartbeatLogSubscriber - include Mongo::Loggable - - def started(event) - log_debug("#{event.address} | STARTED") - end - - def succeeded(event) - log_debug("#{event.address} | SUCCEEDED | #{event.duration}s") - end - - def failed(event) - log_debug("#{event.address} | FAILED | #{event.error.class}: #{event.error.message} | #{event.duration}s") - end - - private - - def logger - Mongo::Logger.logger - end - - def format_message(message) - format("HEARTBEAT | %s".freeze, message) - end - end - -Similarly to command events, the application can subscribe to heartbeat -events globally or for a specific client: - -.. code-block:: ruby - - subscriber = HeartbeatLogSubscriber.new - - Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::SERVER_HEARTBEAT, subscriber) - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test' ) - client.subscribe( Mongo::Monitoring::SERVER_HEARTBEAT, subscriber ) - -Sample output: - -.. code-block:: none - - D, [2018-09-23T13:44:10.707018 #1739] DEBUG -- : HEARTBEAT | 127.0.0.1:27027 | STARTED - D, [2018-09-23T13:44:10.707778 #1739] DEBUG -- : HEARTBEAT | 127.0.0.1:27027 | SUCCEEDED | 0.000772381s - -Heartbeat Event Intervals -------------------------- - -When connected to MongoDB 4.2 and earlier servers, Ruby driver by default -issues heartbeats every ``:heartbeat_frequency`` (Ruby client option) seconds, -and heartbeats are non-overlapping (the succeeded event for a heartbeat is -guaranteed to be published before the started event for the next heartbeat is -published). When connected to MongoDB 4.4 and later servers, the driver uses -multiple monitoring threads and a more complex heartbeat protocol designed -to detect changes in server state quicker; as a result, heartbeat event -intervals can be more irregular and heartbeat events can overlap. Specifically, -an *awaited heartbeat* can start or finish while a *non-awaited heartbeat* -is in progress, and vice versa. Use the ``ServerHeartbeatStarted#awaited?``, -``ServerHeartbeatSucceeded#awaited?`` and ``ServerHeartbeatFailed#awaited?`` -methods to distinguish between non-awaited and awaited heartbeats. - -When a client is attempting to perform an operation and it does not have a -suitable server, the deployment is scanned more frequently - each server can -be polled up to every 500 milliseconds. It is also possible for the application -to request a manual scan of a particular server; the driver enforces the -500 millisecond minimum interval between scans. - -Connection Pool And Connection Monitoring -========================================= - -Each client maintains a connection pool for each server in the deployment that -it is aware of, and publishes events for both connection pools and individual -connections. To subscribe to these events, define a subscriber class implementing -the method ``pubished`` which takes a single parameter for the event that -is being published. Note that future versions of the driver may introduce -additional events published through this mechanism. - -The following events are currently implemented by the driver, following -the `CMAP specification `_: - -- PoolCreated -- PoolCleared -- PoolClosed -- ConnectionCreated -- ConnectionReady -- ConnectionClosed -- ConnectionCheckOutStarted -- ConnectionCheckOutFailed -- ConnectionCheckOutSucceeded -- ConnectionCheckedIn - -The driver provides a logging subscriber which may be used to log all -connection pool and connection-related events. This subscriber is not enabled -by default because it will create log entries for each operation performed -by the application. To enable this subscriber globally or per client: - -.. code-block:: ruby - - Mongo::Monitoring::Global.subscribe( - Mongo::Monitoring::CONNECTION_POOL, - Mongo::Monitoring::CmapLogSubscriber.new) - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test' ) - subscriber = Mongo::Monitoring::CmapLogSubscriber.new - client.subscribe( Mongo::Monitoring::CONNECTION_POOL, subscriber ) - -Sample output: - -.. code-block:: none - - D, [2019-05-06T17:23:21.595412 #8576] DEBUG -- : MONGODB | EVENT: # - D, [2019-05-06T17:23:21.595584 #8576] DEBUG -- : MONGODB | EVENT: # - D, [2019-05-06T17:23:21.603549 #8576] DEBUG -- : MONGODB | EVENT: # - D, [2019-05-06T17:23:21.603616 #8576] DEBUG -- : MONGODB | EVENT: # - D, [2019-05-06T17:23:21.603684 #8576] DEBUG -- : MONGODB | EVENT: # - D, [2019-05-06T17:23:21.604079 #8576] DEBUG -- : MONGODB | EVENT: # - D, [2019-05-06T17:23:21.605759 #8576] DEBUG -- : MONGODB | EVENT: # - D, [2019-05-06T17:23:21.605784 #8576] DEBUG -- : MONGODB | EVENT: # - D, [2019-05-06T17:23:21.605817 #8576] DEBUG -- : MONGODB | EVENT: # - D, [2019-05-06T17:23:21.605852 #8576] DEBUG -- : MONGODB | EVENT: # - - -Disabling Monitoring -==================== - -To turn off monitoring, set the client monitoring option to ``false``: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test', :monitoring => false ) - - -Excluded and Redacted Events -============================ - -The Ruby driver does not pubish, and occasionaly redacts, some events via the -command monitoring mechanism: - -1. If the command belongs to a particular subset of redacted commands, or - contains keys that trigger payload redaction, an empty payload will be - provided for security reasons. The full payload can be accessed by setting - the ``MONGO_RUBY_DRIVER_UNREDACT_EVENTS`` environment variable to ``1``, ``true`` or ``yes``. The - following commands are redacted: - - - ``authenticate`` - - ``saslStart`` - - ``saslContinue`` - - ``getnonce`` - - ``createUser`` - - ``updateUser`` - - ``copydbgetnonce`` - - ``copydbsaslstart`` - - ``copydb`` -2. If the command is a handshake command, either ``ismaster`` or ``hello``, on - a non-monitoring connection, no event is published at all. -3. Commands sent over monitoring connections (such as ismaster and hello) do - not publish command monitoring events. Instead, every time a server is - checked a server heartbeat event is published. The server heartbeat events - do not include command or reply payloads. -4. If the command is a handshake command, and the ``speculativeAuthenticate`` - options is ``true``, the command will be redacted, and an empty payload will - be provided. diff --git a/docs/reference/projection.txt b/docs/reference/projection.txt deleted file mode 100644 index bc38816907..0000000000 --- a/docs/reference/projection.txt +++ /dev/null @@ -1,68 +0,0 @@ -********** -Projection -********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -By default, queries in MongoDB return all fields in matching -documents. To limit the amount of data that MongoDB sends to -applications, you can include a -:manual:`projection` -document in the query operation. - -Projection Document -=================== - -The projection document limits the fields to return for all -matching documents. The projection document can specify the -inclusion of fields or the exclusion of field and has the -following form: - -.. code-block:: javascript - - { 'projection': { field1: , field2: ... } } - -```` may be ``0`` (or ``false``) to exclude the field, or -``1`` (or ``true``) to include it. With the exception of the ``_id`` -field, you may not have both inclusions and exclusions in the same -projection document. - -Examples -======== - -The following code example uses the ``restaurants`` sample dataset. - -To return only the ``name``, ``cuisine`` and ``_id`` fields for -documents that match the query filter, explicitly include the ``name`` -and ``cuisine`` fields in the projection document. The ``_id`` field is -included automatically unless specifically excluded. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test') - collection = client[:restaurants] - - collection.find({}, { 'projection' => - { 'name' => 1, 'cuisine' => 1 } }).limit(5).each do |doc| - p doc - end - -To return ``name`` and ``cuisine`` but exclude all other fields, -including ``_id``, use the following projection document: - -.. code-block:: javascript - - { 'projection' => { 'name' => 1, 'cuisine' => 1, '_id' => 0 } } - - -To return all fields *except* the address field, use the following: - -.. code-block:: javascript - - { 'projection' => { 'address' => 0 } } diff --git a/docs/reference/query-cache.txt b/docs/reference/query-cache.txt deleted file mode 100644 index 772d24ad24..0000000000 --- a/docs/reference/query-cache.txt +++ /dev/null @@ -1,313 +0,0 @@ -*********** -Query Cache -*********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -.. _query-cache: - -The MongoDB Ruby driver provides a built-in query cache. When enabled, the -query cache saves the results of previously-executed find and aggregation -queries. When those same queries are performed again, the driver returns -the cached results to prevent unnecessary roundtrips to the database. - -Usage -===== - -The query cache is disabled by default. It can be enabled on the global -scope as well as within the context of a specific block. The driver also -provides a :ref:`Rack middleware ` to enable the -query cache automatically for each web request. - -To enable the query cache globally: - -.. code-block:: ruby - - Mongo::QueryCache.enabled = true - -Similarly, to disable it globally: - -.. code-block:: ruby - - Mongo::QueryCache.enabled = false - -To enable the query cache within the context of a block: - -.. code-block:: ruby - - Mongo::QueryCache.cache do - Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music') do |client| - client['artists'].find(name: 'Flying Lotus').first - #=> Queries the database and caches the result - - client['artists'].find(name: 'Flying Lotus').first - #=> Returns the previously cached result - end - end - -And to disable the query cache in the context of a block: - -.. code-block:: ruby - - Mongo::QueryCache.uncached do - Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music') do |client| - client['artists'].find(name: 'Flying Lotus').first - #=> Sends the query to the database; does NOT cache the result - - client['artists'].find(name: 'Flying Lotus').first - #=> Queries the database again - end - end - -You may check whether the query cache is enabled at any time by calling -``Mongo::QueryCache.enabled?``, which will return ``true`` or ``false``. - - -Interactions With Fibers -======================== - -The Query cache enablement flag is stored in fiber-local storage (using -`Thread.current `_. -This, in principle, permits query cache state to be per fiber, although -this is not currently tested. - -There are methods in the Ruby standard library, like ``Enumerable#next``, -that `utilize fibers `_ -in their implementation. These methods would not see the query cache -enablement flag when it is set by the applications, and subsequently would -not use the query cache. For example, the following code does not utilize -the query cache despite requesting it: - -.. code-block:: ruby - - Mongo::QueryCache.enabled = true - - client['artists'].find({}, limit: 1).to_enum.next - # Issues the query again. - client['artists'].find({}, limit: 1).to_enum.next - -Rewriting this code to use ``first`` instead of ``next`` would make it use -the query cache: - -.. code-block:: ruby - - Mongo::QueryCache.enabled = true - - client['artists'].find({}, limit: 1).first - # Utilizes the cached result from the first query. - client['artists'].find({}, limit: 1).first - - -.. _query-cache-matching: - -Query Matching -============== - -A query is eligible to use cached results if it matches the original query -that produced the cached results. Two queries are considered matching if they -are identical in the following values: - -* Namespace (the database and collection on which the query was performed) -* Selector (for aggregations, the aggregation pipeline stages) -* Skip -* Sort -* Projection -* Collation -* Read Concern -* Read Preference - -For example, if you perform one query, and then perform a mostly identical query -with a different sort order, those queries will not be considered matching, -and the second query will not use the cached results of the first. - -Limits -====== - -When performing a query with a limit, the query cache will reuse an existing -cached query with a larger limit if one exists. For example: - -.. code-block:: ruby - - Mongo::QueryCache.cache do - Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music') do |client| - client['artists'].find(genre: 'Rock', limit: 10) - #=> Queries the database and caches the result - - client['artists'].find(genre: 'Rock', limit: 5) - #=> Returns the first 5 results from the cached query - - client['artists'].find(genre: 'Rock', limit: 20) - #=> Queries the database again and replaces the previously cached query results - end - end - -Cache Invalidation -================== - -The query cache is cleared in part or in full on every write operation. Most -write operations will clear the results of any queries were performed on the same -collection that is being written to. Some operations will clear the entire -query cache. - -The following operations will clear cached query results on the same database and -collection (including during bulk writes): - -* ``insert_one`` -* ``update_one`` -* ``replace_one`` -* ``update_many`` -* ``delete_one`` -* ``delete_many`` -* ``find_one_and_delete`` -* ``find_one_and_update`` -* ``find_one_and_replace`` - -The following operations will clear the entire query cache: - -* aggregation with ``$merge`` or ``$out`` pipeline stages -* ``commit_transaction`` -* ``abort_transaction`` - -Manual Cache Invalidation -========================= - -You may clear the query cache at any time with the following method: - -.. code-block:: ruby - - Mongo::QueryCache.clear - -This will remove all cached query results. - -Transactions -============ - -Queries are cached within the context of a transaction, but the entire -cache will be cleared when the transaction is committed or aborted. - -.. code-block:: ruby - - Mongo::QueryCache.cache do - Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music') do |client| - session = client.start_session - - session.with_transaction do - client['artists'].insert_one({ name: 'Fleet Foxes' }, session: session) - - client['artists'].find({}, session: session).first - #=> { name: 'Fleet Foxes' } - #=> Queries the database and caches the result - - client['artists'].find({}, session: session).first - #=> { name: 'Fleet Foxes' } - #=> Returns the previously cached result - - session.abort_transaction - end - - client['artists'].find.first - #=> nil - # The query cache was cleared on abort_transaction - end - end - -.. note:: - - Transactions are often performed with a "snapshot" read concern level. Keep - in mind that a query with a "snapshot" read concern cannot return cached - results from a query without the "snapshot" read concern, so it is possible - that a transaction may not use previously cached queries. - - To understand when a query will use a cached result, see the - :ref:`Query Matching ` section. - -Aggregations -============ - -The query cache also caches the results of aggregation pipelines. For example: - -.. code-block:: ruby - - Mongo::QueryCache.cache do - Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music') do |client| - client['artists'].aggregate([ { '$match' => { name: 'Fleet Foxes' } } ]).first - #=> Queries the database and caches the result - - client['artists'].aggregate([ { '$match' => { name: 'Fleet Foxes' } } ]).first - #=> Returns the previously cached result - end - end - -.. note:: - - Aggregation results are cleared from the cache during every write operation, - with no exceptions. - -System Collections -================== - -MongoDB stores system information in collections that use the ``database.system.*`` -namespace pattern. These are called system collections. - -Data in system collections can change due to activity not triggered by the -application (such as internal server processes) and as a result of a variety of -database commands issued by the application. Because of the difficulty of -determining when the cached results for system collections should be expired, -queries on system collections bypass the query cache. - -You may read more about system collections in the -:manual:`MongoDB documentation `. - -.. note :: - - Even when the query cache is enabled, query results from system collections - will not be cached. - - -.. _query-cache-middleware: - -Query Cache Middleware -====================== - -Rack Middleware ---------------- - -The driver provides a Rack middleware which enables the query cache for the -duration of each web request. Below is an example of how to enable the -query cache middleware in a Ruby on Rails application: - -.. code-block:: ruby - - # config/application.rb - - # Add Mongo::QueryCache::Middleware at the bottom of the middleware stack - # or before other middleware that queries MongoDB. - config.middleware.use Mongo::QueryCache::Middleware - -Please refer to the `Rails on Rack guide -`_ -for more information about using Rack middleware in Rails applications. - - -.. _query-cache-active-job-middleware: - -Active Job Middleware ---------------------- - -The driver provides an Active Job middleware which enables the query cache for -each job. Below is an example of how to enable the query cache Active Job -middleware in a Ruby on Rails application: - -.. code-block:: ruby - - # config/application.rb - - ActiveSupport.on_load(:active_job) do - include Mongo::QueryCache::Middleware::ActiveJob - end diff --git a/docs/reference/schema-operations.txt b/docs/reference/schema-operations.txt deleted file mode 100644 index 8d67d569fb..0000000000 --- a/docs/reference/schema-operations.txt +++ /dev/null @@ -1,19 +0,0 @@ -.. _schema-operations: - -***************** -Schema Operations -***************** - -.. default-domain:: mongodb - -This section describes schema-related operations that the driver provides, -including managing databases, collections, indexes and users. - -.. toctree:: - :titlesonly: - - /reference/database-tasks - /reference/collection-tasks - /reference/indexing - /reference/search-indexes - /reference/collations diff --git a/docs/reference/search-indexes.txt b/docs/reference/search-indexes.txt deleted file mode 100644 index 91f99670f7..0000000000 --- a/docs/reference/search-indexes.txt +++ /dev/null @@ -1,129 +0,0 @@ -******************** -Atlas Search Indexes -******************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -If you are using a database hosted by MongoDB Atlas, the driver provides the -ability to create, drop and view `Atlas search indexes `_ -on a collection through the ``search_indexes`` attribute: - -.. code-block:: ruby - - client = Mongo::Client.new(your_atlas_uri, database: 'music') - client[:bands].search_indexes - # => # ...> - - -Creating Search Indexes -======================= - -Search indexes can be created one at a time, or several can be created in -parallel in a single operation. - -To create a single index, use ``search_indexes#create_one``, passing the index -definition as the first argument, and an optional name for the index as the -second argument. - -.. code-block:: ruby - - client[:bands].search_indexes.create_one({ dynamic: true }) - - client[:bands].search_indexes.create_one( - { - dynamic: false, - fields: { - name: { type: 'string', analyzer: 'lucene.simple' } - } - }, - 'band-name-index' - ) - -To create multiple indexes, use ``search_indexes#create_many`` which accepts -an array of index specifications. Unlike ``create_one``, each index -specification is a hash with at least a ``definition`` key, which -defines the index. Each has may also specify a ``name`` key, to name -the index. - -.. code-block:: ruby - - client[:bands].search_indexes.create_many([ - { definition: { dynamic: true } }, - { name: 'band-name-index, - definition: { - dynamic: false, - fields: { - name: { type: 'string', analyzer: 'lucene.simple' } - } - } - }, - ]) - -Note that whether you call ``create_one`` or ``create_many``, the -method will return immediately, before the indexes are created. The -indexes are then created in the background, asynchronously. - - -Update Search Indexes -===================== - -You can programmatically update an Atlas search index. For example, you -might do this to change the analyzer used, or to provide an explicit field -mapping, instead of a dynamic one. To do this, use the ``search_indexes#update_one`` -method: - -.. code-block:: ruby - - client[:bands].search_indexes.update_one(new_definition, id: index_id) - - client[:bands].search_indexes.update_one(new_definition, name: index_name) - -Indexes may be identified by either id, or name, but you must specify one -or the other. The new index definition must be a complete definition--it will -take precedence as specified over the existing definition. - -To get the id or name of an index that you wish to update, you can -`list the search indexes <#listing-search-indexes>`_. - - -Dropping Search Indexes -======================= - -To drop Atlas search indexes, call ``search_indexes#drop_one`` and -provide either the ``id`` or the ``name`` of the index you wish to -drop. - -.. code-block:: ruby - - client[:bands].search_indexes.drop_one(id: index_id) - - client[:bands].search_indexes.drop_one(name: index_name) - -In either case, the method will return immediately and the index will -be dropped in the background, asynchronously. - -To get the id or name of an index that you wish to drop, you can -`list the search indexes <#listing-search-indexes>`_. - - -Listing Search Indexes -====================== - -To list the available search indexes, iterate over the -``search_indexes`` object: - -.. code-block:: ruby - - client[:bands].search_indexes.each do |index_spec| - p index_spec['id'] - p index_spec['name'] - p index_spec['status'] - p index_spec['queryable'] - p index_spec['latestDefinition'] - end diff --git a/docs/reference/sessions.txt b/docs/reference/sessions.txt deleted file mode 100644 index 64e5db6926..0000000000 --- a/docs/reference/sessions.txt +++ /dev/null @@ -1,149 +0,0 @@ -.. _sessions: - -******** -Sessions -******** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - - -Version 3.6 of the MongoDB server introduces the concept of logical sessions for clients. -A session is an abstract concept that represents a set of sequential operations executed -by an application that are related in some way. A session object can be created via a ``Mongo::Client`` -and passed to operation methods that should be executed in the context of that session. - -Please note that session objects are not thread safe. They must only be used by one thread at a time. - -.. _create-session: - -Creating a session from a ``Mongo::Client`` -=========================================== - -A session can be created by calling the ``start_session`` method on a client and passing it a block: - -.. code-block:: ruby - - client.start_session do |session| - # work with the session - end - -When using the block form, the session will be automatically ended by the driver after the block finishes executing. - -It is valid to call ``start_session`` with no options set. This will result in a -session that has no effect on the operations performed in the context of that session, -other than to include a session ID in commands sent to the server. Please see the API docs for all supported -session options. - -An error will be thrown if the driver is connected to a deployment that does not support sessions and the -``start_session`` method is called. - -Note that server sessions are discarded server-side if not used for a certain period of time. -Be aware that if the application calls ``#start_session`` on a client and waits more than 1 minute to use -the session, it risks getting errors due to the session going stale before it is used. - - - -Using a session -=============== -A session object can be passed to most driver methods so that the operation can be executed in the -context of that session. Please see the API docs for which methods support a session argument. - -Create a session and execute an insert, then a find using that session: - -.. code-block:: ruby - - client.start_session do |session| - client[:artists].insert_one({ :name => 'FKA Twigs' }, session: session) - client[:artists].find({ :name => 'FKA Twigs' }, limit: 1, session: session).first - end - -If you like to call methods on a ``Mongo::Collection::View`` in the context of a particular session, you can create the -``Mongo::Collection::View`` with the session and then call methods on it: - -.. code-block:: ruby - - client.start_session(causal_consistency: true) do |session| - view = client[:artists].find({ :name => 'FKA Twigs' }, session: session) - view.count # will use the session - end - -You can also pass the session option to the methods directly. This session will override any session associated with -the ``Mongo::Collection::View``: - -.. code-block:: ruby - - client.start_session do |session| - client.start_session do |second_session| - view = client[:artists].find({ :name => 'FKA Twigs' }, session: session) - view.count(session: second_session) # will use the second_session - end - end - -Alternative way to create a session -=================================== - -A session can be created by calling the ``start_session`` method on a client: - -.. code-block:: ruby - - session = client.start_session - -When ``start_session`` is used without passing a block to it, the driver does not automatically clean up the session which can result in an accumulation of sessions on the server. Use `end_session <#end-a-session>`_ to manually end the session created. The server will automatically clean up old sessions after a timeout but the application should end sessions when the sessions are no longer needed. - -Unacknowledged Writes -===================== - -Unacknowledged writes are only allowed outside the session mechanism; if an explicit session is supplied for an -unacknowledged write, the driver will not send the session id with the operation. Similarly, the driver will not use -an implicit session for an unacknowledged write. - -Causal Consistency -================== -A causally consistent session will let you read your writes and guarantee monotonically increasing -reads from secondaries. -To create a causally consistent session, set the ``causal_consistency`` option to true: - -.. code-block:: ruby - - session = client.start_session(causal_consistency: true) - - # The update message goes to the primary. - collection = client[:artists] - collection.update_one({ '_id' => 1 }, { '$set' => { 'x' => 0 } }, session: session) - - # Read your write, even when reading from a secondary! - collection.find({ '_id' => 1 }, session: session).first - - # This query returns data at least as new as the previous query, - # even if it chooses a different secondary. - collection.find({ '_id' => 2 }, session: session).first - -Since unacknowledged writes don't receive a response from the server (or don't wait for a response), the driver -has no way of keeping track of where the unacknowledged write is in logical time. Therefore, causally -consistent reads are not causally consistent with unacknowledged writes. - -Note that if you set the causal_consistency option to nil as in ``(causal_consistency: nil)``, it will be interpreted -as false. - -.. _end-session: - -End a session -============= -To end a session, call the ``end_session`` method: - -.. code-block:: ruby - - session.end_session - -The Ruby driver will then add the id for the corresponding server session to a pool for reuse. -When a client is closed, the driver will send a command to the server to end all sessions it has cached -in its server session pool. You may see this command in your logs when a client is closed. - -Note that when using the `block syntax <#creating-a-session-from-a-mongo-client>`_ for ``start_session`` the session is automatically ended after -the block finishes executing. diff --git a/docs/reference/text-search.txt b/docs/reference/text-search.txt deleted file mode 100644 index de2e09837f..0000000000 --- a/docs/reference/text-search.txt +++ /dev/null @@ -1,51 +0,0 @@ -*********** -Text Search -*********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -MongoDB provides :manual:`text indexes ` -to support text search queries on string content. Text indexes -can include any field whose value is a string or an array of -string elements. - -.. note:: - - MongoDB Atlas also provides - `Atlas Search `_ - which is a more powerful and flexible text search solution. - The rest of this page discusses text indexes and not Atlas Search. - -To perform a text search with the Ruby driver, first create a text -index with ``indexes.create_one()``. The following command creates a -text index on the ``name`` field of the ``restaurants`` collection in -the ``test`` database. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test') - client['restaurants'].indexes.create_one( { :name => 'text' } ) - -Once the text index is created you can use it as part of a query. The -following code finds all documents in the ``restaurants`` collection -which contain the word ``garden``, without case sensitivity. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test') - client[:restaurants].find( - { '$text' => - { '$search' => 'garden', '$caseSensitive' => false } - } - ).each do |document| - - #=> Yields a BSON::Document. - - end - diff --git a/docs/reference/transactions.txt b/docs/reference/transactions.txt deleted file mode 100644 index 722038ca8f..0000000000 --- a/docs/reference/transactions.txt +++ /dev/null @@ -1,207 +0,0 @@ -************ -Transactions -************ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - - -Version 4.0 of the MongoDB server introduces -`multi-document transactions `_. -(Updates to multiple fields within a single document are atomic in all -versions of MongoDB.) Ruby driver version 2.6.0 adds support for transactions. - -.. _using-transactions: - -Using Transactions -================== - -In order to start a transaction, the application must have a :ref:`session `. - -The recommended way to use transactions is to utilize the ``with_transaction`` -helper method: - -.. code-block:: ruby - - session = client.start_session - session.with_transaction do - collection.insert_one({hello: 'world'}, session: session) - end - -The ``with_transaction`` helper does the following: - -- It starts a transaction prior to calling the supplied block, and commits - the transaction when the block finishes. -- If any of the operations in the block, or the commit operation, result in - a transient transaction error, the block and/or the commit will be executed - again. - -The block should be idempotent, because it may be called multiple times. - -The block may explicitly commit or abort the transaction, by calling -``commit_transaction`` or ``abort_transaction``; in this case ``with_transaction`` -will not attempt to commit or abort (but may still retry the block on -transient transaction errors propagated out of the block). - -The block will also be retried if the transaction's commit result is unknown. -This may happen, for example, if the cluster undergoes an election during the -commit. In this case when the block is retried, the primary server of the -topology would likely have changed. - -Currently ``with_transaction`` will stop retrying the block and the commit once -120 seconds pass since the beginning of its execution. This time is not -configurable and may change in a future driver version. Note that this -does not guarantee the overall runtime of ``with_transactions`` will be 120 -seconds or less - just that once 120 seconds of wall clock time have elapsed, -further retry attempts will not be initiated. - -A low level API is also available if more control over transactions is desired. - -``with_transaction`` takes the same options as ``start_transaction`` does, -which are read concern, write concern and read preference: - -.. code-block:: ruby - - session = client.start_session - session.with_transaction( - read_concern: {level: :majority}, - write_concern: {w: 3}, - read: {mode: :primary} - ) do - collection.insert_one({hello: 'world'}, session: session) - end - -Handling Errors Within the ``with_transaction`` Block ------------------------------------------------------ - -If a command inside the ``with_transaction`` block fails, it may cause -the transaction on the server to be aborted. This situation is normally handled -transparently by the driver. However, if the application catches such an error -and does not re-raise it, the driver will not be able to determine whether -the transaction was aborted or not. The driver will then retry the block -indefinitely. - -To avoid this situation, the application must not silently handle errors within -``with_transaction`` block. If the application needs to handle errors within -the block, it must re-raise the errors. - -.. code-block:: ruby - - session.with_transaction do - collection.insert_one({hello: 'world'}, session: session) - rescue Mongo::Error::OperationFailure => e - # Do something in response to the error - raise e - end - -If the applications needs to handle errors in a custom way, it should use -the low level API instead. - -Low Level API -============= - -A transaction can be started by calling the ``start_transaction`` method on a session: - -.. code-block:: ruby - - session = client.start_session - session.start_transaction - -It is also possible to specify read concern, write concern and read preference -when starting a transaction: - -.. code-block:: ruby - - session = client.start_session - session.start_transaction( - read_concern: {level: :majority}, - write_concern: {w: 3}, - read: {mode: :primary}) - -To persist changes made in a transaction to the database, the transaction -must be explicitly committed. If a session ends with an open transaction, -`the transaction is aborted `_. -A transaction may also be aborted explicitly. - -To commit or abort a transaction, call ``commit_transaction`` or -``abort_transaction`` on the session instance: - -.. code-block:: ruby - - session.commit_transaction - - session.abort_transaction - -Note: an outstanding transaction can hold locks to various objects in the -server, such as the database. For example, the drop call in the following -snippet will hang for `transactionLifetimeLimitSeconds -`_ -seconds (default 60) until the server expires and aborts the transaction: - -.. code-block:: ruby - - c1 = Mongo::Client.new(['127.0.0.1:27017']).use(:test_db) - session = c1.start_session - c1['foo'].insert_one(test: 1) - session.start_transaction - c1['foo'].insert_one({test: 2}, session: session) - - c2 = Mongo::Client.new(['127.0.0.1:27017']).use(:test_db) - # hangs - c2.database.drop - -Since transactions are associated with server-side sessions, closing the client -does not abort a transaction that this client initiated - the application must -either call ``abort_transaction`` or wait for the transaction to time out on -the server side. In addition to committing or aborting the transaction, an -application can also end the session which will abort a transaction on this -session if one is in progress: - -.. code-block:: ruby - - session.end_session - - c2 = Mongo::Client.new(['127.0.0.1:27017']).use(:test_db) - # ok - c2.database.drop - -Handling Errors ---------------- - -If a command inside the transaction fails, the transaction may be aborted -on the server. Errors that abort transactions do not have -``TransientTransactionError`` in their error labels. An attempt to commit such a -transaction will be rejected with ``NoSuchTransaction`` error. - - -Retrying Commits -================ - -The transaction commit `can be retried -`_ -if it fails. Here is the Ruby code to do so: - -.. code-block:: ruby - - begin - session.commit_transaction - rescue Mongo::Error => e - if e.label?('UnknownTransactionCommitResult') - retry - else - raise - end - end - - -Transaction Nesting -=================== - -MongoDB does not support nesting transactions. Attempting to call -``start_transaction`` or ``with_transaction`` when a transaction is already -in progress will result in an error. diff --git a/docs/reference/user-management.txt b/docs/reference/user-management.txt deleted file mode 100644 index 5363b9a2fc..0000000000 --- a/docs/reference/user-management.txt +++ /dev/null @@ -1,224 +0,0 @@ -.. _user-management: - -*************** -User Management -*************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -The Mongo Ruby Driver provides a set of methods for managing users in a -MongoDB deployment. All of these methods are defined on the -``Mongo::Auth::User::View`` class, which defines the behavior for -performing user-related operations on a database. You can access a database's -user view by calling the ``users`` method on the correpsonding -``Mongo::Database`` object: - -.. code-block:: ruby - - client.database.users - -Note that this will open a view on the database to which the client is already -connected. To interact with the users defined on a different database, call -the client's ``use`` method and pass in the name of the database with which -you want to connect: - -.. code-block:: ruby - - client.use(:users).database.users - -In this example, all operations would be performed on the ``users`` database. - -For more information about users and user management, see MongoDB's -:manual:`online documentation `. - - -Users and Databases -=================== - -When a client connects to the server, MongoDB distinguishes the database -that the client will perform operations on from the :ref:`auth source ` -which is the database storing the user that the client is authenticating as. - -In many cases, the auth source is the same as the database. When they differ, -user management operations must be done on the auth source database. For -example, to create a user authenticating with X.509 certifcate, which must be -defined on the ``$external`` database: - -.. code-block:: ruby - - client.use('$external').database.users.create( - 'C=US,ST=New York,L=New York City,O=MongoDB,OU=x509,CN=localhost', - roles: [{role: 'read', db: 'admin'}], - ) - -Note that the auth source is not specified for creating the user - auth source -is only used during the authentication process. If ``#create`` is invoked with -a ``User`` object with ``auth_source`` set, the auth source is ignored for -the purposes of user management. - - -Creating Users -============== - -There are two ways to create a new database user with the Ruby Driver. - -The simplest way to create a new user is to use the ``create`` method, -passing in a username, password, and roles: - -.. code-block:: ruby - - client.database.users.create( - 'alanturing', - password: 'enigma', - roles: [ Mongo::Auth::Roles::READWRITE ] - ) - -Another way to create a user is to first create a ``Mongo::Auth::User`` object -with all the user information and then pass that object into the ``create`` -method instead. - -.. code-block:: ruby - - user = Mongo::User.new( - user: 'alanturing', - password: 'enigma', - roles: [ Mongo::Auth::Roles::READWRITE ] - ) - - client.database.users.create(user) - -Note that your new user's credentials will be stored in whatever database your -``client`` object is currently connected to. This will be your user's -``auth_source``, and you must be connected to that same database in order to -update, remove, or get information about the user you just created in the future. - -The ``create`` method takes a ``Hash`` of options as an optional second argument. -The ``:roles`` option allows you to grant permissions to the new user. -For example, the ``Mongo::Auth::Roles::READ_WRITE`` role grants the user the -ability to both read from and write to the database in which they were created. -Each role can be specified as a ``String`` or as a ``Hash``. If you would like -to grant permissions to a user on a database other than the one on which they -were created, you can pass that database name in the role ``Hash``. To create -a user ``alanturing`` with permission to read and write on the ``machines`` -database, you could execute the following code: - -.. code-block:: ruby - - client.database.users.create( - 'alanturing', - password: 'enigma', - roles: [{ role: Mongo::Auth::Roles::READWRITE, db: 'machines' }] - ) - -For more information about roles in MongoDB, see the -:manual:`Built-in roles` documentation. - -In addition to the ``:roles`` option, the ``create`` method supports a -``:session`` option, which allows you to specify a ``Mongo::Session`` object -to use for this operation, as well as a ``:write_concern`` option, -which specifies the write concern of this operation when performed on a -replica set. - -.. seealso:: - :manual:`Built-in roles` - :manual:`Write Concerns`, - :ref:`Sessions`, - - -User Information -================ - -To view information about a user that already exists in the database, use the -``info`` method: - -.. code-block:: ruby - - client.database.users.info('alanturing') - -If the user exists, this method will return an ``Array`` object containing a -``Hash`` with information about the user, such as their id, username, the -database they were created on, and their roles. If the user doesn't exist, -this method will return an empty Array. - -The ``info`` method also takes an optional ``Hash`` of options as a second -argument. Currently, the only supported option is ``:session``, which allows -you to specify a ``Mongo::Session`` object to use for this operation. - -The Ruby Driver does not have a method that lists all of the users that -currently exist in a database. - -.. seealso:: - :ref:`Sessions ` - - -Updating Users -============== - -To update a user that already exists in the database, you can use the -``update`` method in one of two ways. The first way is to specify the name of -the user you wish to update, along with a new set of options. - -.. warning:: - - You must include all user options in the options ``Hash``, even those options - whose values will remain the same. Omitting an option is the same as setting - it to an empty value. - -.. code-block:: ruby - - client.database.users.update( - 'alanturing', - roles: [ Mongo::Auth::Roles::READ_WRITE ] - password: 'turing-test' - ) - -The second way to update a user is to pass an updated ``Mongo::Auth::User`` -object to the ``update`` method in lieu of a username. - -.. code-block:: ruby - - user = Mongo::Auth::User.new({ - user: 'alanturing', - roles: [ Mongo::Auth::Roles::READ_WRITE ], - password: 'turing-test' - }) - - client.database.users.update(user) - -Optionally, the ``update`` method takes a ``Hash`` of options as a second -argument. The two possible options for this method are ``:session``, which -allows you to specify a ``Mongo::Session`` object on which to perform this -operation, and ``:write_concern``, which sets a write concern if this operation -is performed on a replica set. - -.. seealso:: - :ref:`Sessions` - :manual:`Write Concerns`, - -Removing Users -============== - -To remove a user from the database, use the ``remove`` method: - -.. code-block:: ruby - - client.database.users.remove('alanturing') - -You may pass a ``Hash`` of options as a second argument. The two supported -options for the ``remove`` method are ``:session`` and ``:write_concern``. -``:session`` allows you to specify a ``Mongo::Session`` object to use for -this operation. ``:write_concern`` specifies the write concern -of the operation if you are running this command against a replica set. - -The Ruby Driver does not provide a method for removing all users -from a database. - -.. seealso:: - :ref:`Sessions` - :manual:`Write Concerns`, diff --git a/docs/reference/working-with-data.txt b/docs/reference/working-with-data.txt deleted file mode 100644 index 0f36c6be47..0000000000 --- a/docs/reference/working-with-data.txt +++ /dev/null @@ -1,27 +0,0 @@ -.. _working-with-data: - -***************** -Working With Data -***************** - -.. default-domain:: mongodb - -This section describes in detail the functionality that the Ruby driver -implements for inserting, updating and retrieving data from MongoDB. - -.. toctree:: - :titlesonly: - - /reference/crud-operations - /reference/bulk-operations - /reference/projection - /reference/aggregation - /reference/map-reduce - /reference/text-search - /reference/geospatial-search - /reference/query-cache - /reference/gridfs - /reference/change-streams - /reference/sessions - /reference/transactions - /reference/in-use-encryption diff --git a/docs/release-notes.txt b/docs/release-notes.txt deleted file mode 100644 index 33f251ff6f..0000000000 --- a/docs/release-notes.txt +++ /dev/null @@ -1,411 +0,0 @@ -.. _release-notes: - -************* -Release Notes -************* - -.. default-domain:: mongodb - -This page documents significant changes in driver releases. - -It is not an exhaustive list of changes and generally does not enumerate -bug fixes; please consult the `releases page on GitHub -`_ for a more -comprehensive list of changes in each version of the driver and the -`releases page in Jira -`_ -for the complete list of changes, including those internal to the driver and -its test suite. - - -.. _release-notes-2.20: - -2.20 -==== - -This release includes the following new features: - -- Support for Ruby 2.5 and 2.6 has been discontinued. Support for Ruby 2.7 and - JRuby 9.2 has been deprecated, and will be discontinued in the next minor - driver version. Support for JRuby 9.4 has been added. -- Support for the newly-released Ruby-BSON version 5.0. -- Connection strings no longer require a slash between the hosts and the - options. E.g., "mongodb://example.com?w=1" and "mongodb://example.com/?w=1" - are both valid connection strings now. -- Container runtime and orchestration metadata for the client environment are - now sent to the server for analytics purposes. -- A warning message is now written to the log when the host is detected to be - a CosmosDB (Azure) or DocumentDB (Amazon) instance. -- When attempting a retry of a read or write operation in a sharded topology, - the retry will be attempted on a different mongos instance, if possible. - - -.. _release-notes-2.19: - -2.19 -==== - -This release of the Ruby driver supports MongoDB version 7.0. The Ruby driver -now supports Ruby 3.2. Ruby 2.5 and 2.6 are now deprecated. - -This release includes the following new features: - -- The driver now limits the number of connections established by a connection - pool simultaneously. By default the limit is 2. The limit can be configured - with the ``:max_connecting`` option of the ``Mongo::Client`` constructor. - The default should be sufficient for most applications. However, if your - application is using a large number of threads, you may need to increase - the limit. -- Added support for automatic AWS credentials retrieval and authentication - with temporary credentials when AWS KMS is used for client side encryption. -- Added support for automatic GCP credentials retrieval when Google Cloud Key - Management is used for client side encryption. -- Added support the Azure VM-assigned Managed Identity for Automatic KMS Credentials - when Azure Key Vault is used for client side encryption. -- `Queryable Encryption `_ support is extended. -- Added support for Queryable Encryption Range Indexes. -- A `crypt_shared `_ - library can be now used instead of ``mongocryptd``. -- Added support for AWS IAM Roles for service accounts, EKS in particular. -- AWS Credentials are now cached when possible. -- Added support for creating and managing `Atlas search indexes `_ via the - Ruby driver. - -.. _release-notes-2.18: - -2.18 -==== - -This release of the Ruby driver supports MongoDB version 5.2 and 6.0. - -This release includes the following new features: - -- Added support for `queryable encryption `_. -- Added support for Azure Key Vault, Google Cloud Key Management, and any - KMIP compliant Key Management System to be used as master key storage for - client side encryption. -- It is now possible to provide the :ref:`path to a schema map file ` - instead of the entire schema map as an object. -- The driver now implements the :ref:`feature flag ` mechanism - for incompatible changes and bug fixes. Changes gated behind feature flags - for 2.18 are passing view filter and options to ``aggregate`` and validation - of correct usage of ``update`` vs ``replace`` methods, as described below. -- Added the ``validate_update_replace`` feature flag which validates the - parameters to update and replace operations. If this flag is turned on, an - error will be raised on an invalid update or replacement document. -- Added the ``broken_view_options`` feature flag which allows the view options - to be correctly propagated to the ``aggregate``, ``count``, ``count_documents``, - ``distinct``, and ``estimated_document_count`` mehods. When this flag is - switched on, the view options will be ignored in those methods. -- The driver now permits :ref:`inserting documents with dollar-prefixed and - dotted keys `. -- CRUD methods, methods for listing databases, collection, and indexes management - methods now support a new option ``:comment``. This option enables users to - specify an arbitrary comment to help trace the operation through the - database profiler, currentOp and logs. -- The ``estimated_document_count`` method is now using the ``count`` server - command instead of ``$collStats`` aggregation pipeline stage, to support - operation on views. Applications using the Stable API should upgrade to - server versions 5.0.8 (if using MongoDB 5.0) or 5.3.2 (if using MongoDB - 5.1/5.2/5.3) or newer to use the ``count`` command when API strict is enabled, - or avoid setting ``api_strict: true`` when constructing ``Mongo::Client`` - instances with server versions 5.0.0-5.0.7 and 5.1.0-5.3.1. -- The DBRef class has been moved to ``bson-ruby``. For backwards compatibility, - ``BSON::DBRef`` is aliased as ``Mongo::DBRef``. The ``BSON::DBRef`` class - derives from ``BSON::Document``, unlike the legacy ``Mongo::DBRef`` which - derived from ``Object``. ``BSON::DBRef`` retains all attributes passed into - its constructor, unlike ``Mongo::DBRef`` which only allowed ``$ref``, - ``$id``, and ``$db``. ``BSON::DBRef`` also reorders the fields if - necessary to place ``$ref``, ``$id``, and ``$db`` first, in that order, as - required by the MongoDB server. -- ``BulkWrite::Result`` class now has the ``acknowledged?`` attribute. -- Providing an empty array of operations to the bulk write is now an error. -- BSON serialization performance has been improved. -- :ref:`ActiveJob middleware ` was added to - the query cache. -- ``:authorized_collections`` options is recognized when listing collections. -- ``:wildcard_projection`` option was added to the allowed index specification. -- Added ``:srv_max_hosts``/``srvMaxHosts`` Ruby and URI options to limit how - many mongos routers the driver will establish connections to. -- Custom SRV service names are now supported with the ``:srv_service_name`` - Ruby option and the ``srvServiceName`` URI option. -- When 0 is given as the max connection pool size, it is now interpreted to - mean no limit. -- The default maximum connection pool size has been increased to 20 from 5. - -This release adds supports for JRuby 9.3. - - -.. _release-notes-2.17: - -2.17 -==== - -This release of the Ruby driver supports MongoDB version 5.1. It also increases -the minimum required Ruby version to 2.5, and drops support for MongoDB -versions older than 3.6. - -This release includes the following new features: - -- Added new readConcern level "snapshot" (non-speculative) for read commands outside of transactions, including on secondaries. -- Support $merge and $out executing on secondaries. -- Support 'let' option for aggregate and CRUD commands. - -The following bugs were fixed: - -- Push monitor thread can exit when address resolution fails. - -The following non-breaking changes were made: - -- mapReduce command is now deprecated. -- Avoid tight looping in push monitor - - -.. _release-notes-2.16: - -2.16 -==== - -This release adds the following new feature: - -- Load balancer support. - -The following minor improvement has been made: - -- GridFS file retrieval no longer requires index creation privileges when - the indexes already exist, and is thus usable with users that have only - read permissions. - -This release of the Ruby driver increases the minimum required Ruby version -to 2.4 and deprecates support for MongoDB server versions below 3.6. - - -.. _release-notes-2.15: - -2.15 -==== - -This release adds the following new features: - -- Ruby 3.0 support. -- Ability to specify the :ref:`server API parameters `. -- Support for Zstandard and Snappy :ref:`wire protocol compression `. -- :ref:`Query cache middleware ` was moved to the - driver from Mongoid and is now usable in applications that do not use Mongoid. -- It is now possible to create collections with time-series options. -- Experimental support for `MongoDB Atlas Serverless - `_ when not using a - load balancer. - -The following smaller improvements have been made: - -- The ``OperationFailure`` exception message now contains the server error code - name, if provided by the server. The layout of the message was changed to - accommodate the error code name. -- The generic SSL messaging has been removed from ``SocketError`` messages - when TLS is used. TLS connections to MongoDB are now the norm, with Atlas - requiring TLS, and it is more likely that a connection fails due to failed - certificate verification than due to the server not having TLS enabled. -- A hook was added to permit applications to :ref:`modify the TLS context - ` used for TLS connections, for example to exclude - ciphers. -- Heartbeat succeeded and heartbeat failed :ref:`server monitoring events - ` are now linked to the respective heartbeat started - event, to improve usability. -- ``skip`` and ``limit`` options are now prohibited when calling - ``estimated_document_count``, because the server command does not accept them. -- The driver will now omit command monitoring reply payloads when they are - in response to sensitive commands. -- When the driver closes network sockets it now enforces the socket timeout. -- ``estimated_document_count`` collection method now uses the ``$collStats`` - aggregation pipeline stage instead of the count command on 5.0 and newer - servers. -- The platform metadata sent by the driver to the server in the handshake - now includes the purpose of the connection being established, permitting - administrators to distinguish monitoring connections from application - connections. -- The driver now uses monotonic clock for timeouts. -- The driver will no longer mark servers unknown based on errors in - ``writeErrors`` field in the server response. -- Server selection timeout for ``mongocryptd`` has been increased to 10 seconds. - - -.. _release-notes-2.14: - -2.14 -==== - -This release adds the following new features: - -- Queries against Atlas Data Lake are now supported. -- The :ref:`query cache ` has been moved from Mongoid into the - driver. Mongoid will use the driver's query cache as of driver 2.14. - As part of the move, several issues with the query cache have been fixed - and its functionality was extended to cover aggregation pipeline queries - and to support result sets of any size. -- Explain verbosity can now :ref:`be specified ` when explaining. -- Mixed case read preference tag names are now supported. -- The driver will perform :ref:`OCSP endpoint verification ` - by default when TLS is enabled. Due to lack of support in Ruby's ``openssl`` - extension, OCSP stapling is not yet implemented. - -The following smaller improvements have been made: - -- Default logger level for ``Client`` objects is now info (up from debug). - This reduces the amount of log output produced by the driver by default. -- Database and collection write methods support specifying write concern for - the individual operations. -- ``Client#summary`` method now shows the monitoring state of each server. -- When objects other than hashes are attempted to be inserted (which is not - allowed), the driver now provides better diagnostics. -- DNS queries for SRV URIs are now subject to configured socket timeouts. -- When the ``Client`` object is reconnected, session pools are now cleared. - -Support for Ruby versions 2.3 and 2.4 has been deprecated as of this release. - - -.. _release-notes-2.13: - -2.13 -==== - -This release implements the necessary client-side functionality to use the -features added in MongoDB 4.4. Specifically, the following new driver -functionality has been added: - -- Support for the ``directConnection`` URI option to provide a consistent - cross-driver mechanims to discover deployment topology or force direct - connection. -- Support for :ref:`MONGODB-AWS authentication mechanism `. -- When SCRAM authentication is used with 4.4 and newer servers, the driver will - complete authentication with fewer network roundtrips. -- The driver creates an additional monitoring connection for 4.4 and newer - servers, permitting the server to notify the driver when its state changes. - This reduces the time for the driver to discover the new primary during - failover events. -- ``Client`` constructor can be given a block, in which case the client object - will be yielded to the block and automatically closed when the block ends. -- ``start_session`` can be given a block, in which case the session object will - be yielded to the block and automatically ended when the block ends. -- Write options can now be specified for individual CRUD operations. -- The ``:allow_disk_use`` option was added to find operations. -- The ``:authorized_databases`` option was added to ``list_databases`` - method. -- The ``list_collections`` method now passes through all options. -- Ability to set an index :ref:`as hidden ` when creating it. -- Ability to specify commit quorum when creating indexes. -- ``:wrapping_libraries`` :ref:`client option `, to be used - by libraries like Mongoid which wrap the driver to report their version to - the server for troubleshooting/statistics aggregation purposes. - -The following smaller improvements have been made: - -- ``count_documents`` can now be invoked with no arguments. -- The default TCP keep-alive time has been reduced to make the driver - correctly detect dropped connections on Microsoft Azure. -- ``CursorNotFound`` is now a resumable change stream error. -- The number of backtrace lines in exceptions handled by background threads - can now be configured. - - -.. _release-notes-2.12: - -2.12 -==== - -This release adds the following new features: - -- :ref:`Client-side encryption `. -- ``list_collections`` method now accepts the ``:filter`` option. - -The following smaller improvements have been made: - -- Authentication exceptions now include server information to aid in - troubleshooting. - - -.. _release-notes-2.11: - -2.11 -==== - -This release adds the following new features: - -- If a minimum connection pool size is specified, the pool for each server - will create a background thread to eagerly establish connections up to - the specified minimum pool size. -- If the driver connects to the deployment using a SRV URI and the deployment - is a sharded cluster, the driver will poll the SRV DNS records to - automatically discover new and removed mongos servers and adjust the - set of known servers accordingly. - -The following smaller improvements have been made: - -- The driver now permits unencoded subdelimiters in usernames and passwords in - MongoDB URIs. -- User management helpers now accept the write concern option. -- The :ref:`command monitoring ` logger provided with the - driver will now log connection ids used for each command. -- When legacy read retries are used, retry on the same set of server errors - that the modern retries would have retried on. -- The ``distinct(nil)`` call is prohibited because it is rejected by MongoDB - 4.4 and newer servers. - -This release of the Ruby driver increases the minimum required Ruby version -to 2.3, as well as minimum supported JRuby version to 9.2. - - -.. _release-notes-2.10: - -2.10 -==== - -This release implements the necessary client-side functionality to use the -features added in MongoDB 4.2. Specifically, the following new driver -functionality has been added: - -- Support for sharded transactions. -- Applications can set the ``:max_time_ms`` option in ``commit_transaction`` - method. -- Support for database-level aggregation. -- Support for ``$merge`` aggregation pipeline stage. -- The update operations now accept an aggregation pipeline as an array. -- TLS regenotiation is now disabled when possible. -- Change streams now handle post-batch resume tokens provided by the server. - -The following smaller improvements have been made: - -- All methods now accept ``:write_concern`` option for the write concern, - including those that previously accepted the ``:write`` option. -- The query string in a MongoDB URI can now start with ``&``. - -Support for Ruby versions less than 2.3 is deprecated in this release. - - -.. _release-notes-2.9: - -2.9 -=== - -This release adds the following new features: - -- A rewrite of the connection pool code with improved monitoring, - compliant with the CMAP specification -- A modern retryable reads implementation compliant with the cross-driver - retryable reads specification, enabled by default. -- Modern retryable writes are now enabled by default. -- Legacy retryable writes can be disabled in most cases. -- The driver now supports certificate chains being provided as client - certificates for TLS connections. -- Ability to specify multiple CA certificates when creating a ``Client``. -- Ability to pass the private key and certificate via URI options. - -The following smaller improvements have been made: - -- Support for the ``startAfter`` option in the ``$changeStream`` - aggregation pipeline stage. -- Field order of BSON documents sent to the server changed for better logging. -- Certificate paths with unescaped slashes can now be specified in - MongoDB URIs. - -This release deprecates support for Ruby versions less than 2.3. diff --git a/docs/support.txt b/docs/support.txt deleted file mode 100644 index b06675889a..0000000000 --- a/docs/support.txt +++ /dev/null @@ -1,15 +0,0 @@ -******* -Support -******* - -.. default-domain:: mongodb - -Commercial support for the Ruby driver is available through the -`MongoDB Support Portal `_. - -For questions, discussions or general technical support, please visit the -`MongoDB Community Forum -`_. - -Please see :manual:`Technical Support ` page -in the documentation for other support resources. diff --git a/docs/tutorials.txt b/docs/tutorials.txt deleted file mode 100644 index 143f8edbd5..0000000000 --- a/docs/tutorials.txt +++ /dev/null @@ -1,19 +0,0 @@ -.. _tutorials: - -********* -Tutorials -********* - -.. default-domain:: mongodb - -The tutorials in this section provide examples of some frequently used -operations. This section is not meant to be an exhaustive list of all -operations available in the Ruby driver. - -.. toctree:: - :titlesonly: - - Quick Start - tutorials/common-errors - tutorials/bson - diff --git a/docs/tutorials/common-errors.txt b/docs/tutorials/common-errors.txt deleted file mode 100644 index 0651442cb7..0000000000 --- a/docs/tutorials/common-errors.txt +++ /dev/null @@ -1,122 +0,0 @@ -************* -Common Errors -************* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -No Server is Available Matching Preference -========================================== - -If you receive the following error: - -.. code-block:: ruby - - > client[:contacts].insert_one({ vpa: 'cool@cool'}) - Mongo::Error::NoServerAvailable: No server is available matching preference: # using server_selection_timeout=30 and local_threshold=0.015 - from /rvm/gems/ruby-2.4.2/gems/mongo-2.5.0/lib/mongo/server_selector/selectable.rb:115:in `select_server' - from /rvm/gems/ruby-2.4.2/gems/mongo-2.5.0/lib/mongo/cluster.rb:246:in `next_primary' - from /rvm/gems/ruby-2.4.2/gems/mongo-2.5.0/lib/mongo/retryable.rb:150:in `legacy_write_with_retry' - from /rvm/gems/ruby-2.4.2/gems/mongo-2.5.0/lib/mongo/retryable.rb:103:in `write_with_retry' - from /rvm/gems/ruby-2.4.2/gems/mongo-2.5.0/lib/mongo/collection.rb:422:in `block in insert_one' - from /rvm/gems/ruby-2.4.2/gems/mongo-2.5.0/lib/mongo/client.rb:485:in `with_session' - from /rvm/gems/ruby-2.4.2/gems/mongo-2.5.0/lib/mongo/collection.rb:421:in `insert_one' - from (irb):6 - from /rvm/rubies/ruby-2.4.2/bin/irb:11:in `
' - -This error was produced on Ruby Driver version 2.6.x and earlier. This error is -raised when the driver is unable to connect to the server. In order to solve -this, you can try the following: - -- Update the driver to the most recent 2.x release. -- Ensure that the port number is correct in the URI/connection string, or the - host list. -- If you are connecting to Atlas, ensure that the firewall rules are configured - correctly. See the documentation on `Cluster Security `_. -- If your application uses a forking web server, see the documentation on :ref:`Forking `. - -See more on this issue here: -`RUBY-1281 `_. - - -``OperationFailure`` When Reading ``local`` Database With Auth On -================================================================= - -If you are getting the following error when trying to read the ``local`` database: - -.. code-block:: ruby - - Mongo::Error::OperationFailure: not authorized on local to execute command { find: "oplog.rs", filter: { ts: - { $gte: Timestamp 1497449043000|0 } }, sort: { $natural: 1 } } (13) - -You can fix this by adjusting the roles your user has and ensuring that it has -privileges to the local database. If you specifically want to access the oplog, -you can also create a custom role with read access to the local database's -``oplog.rs`` collection. You can find more information about role management -`here `_. - -You can find more information about this issue here: -`MONGOID-4446 `_. - - -``SocketTimeoutError`` and name resolution errors on EC2 -======================================================== - -Some users reported getting the following error: - -.. code-block:: ruby - - Exception - Mongo::Error::SocketTimeoutError - Error - execution expired - /var/lib/gems/2.3.0/gems/mongo-2.5.0/lib/mongo/socket/ssl.rb:57:in `pack_sockaddr_in' - /var/lib/gems/2.3.0/gems/mongo-2.5.0/lib/mongo/socket/ssl.rb:57:in `block (2 levels) in connect!' - /var/lib/gems/2.3.0/gems/mongo-2.5.0/lib/mongo/socket.rb:199:in `handle_errors' - /var/lib/gems/2.3.0/gems/mongo-2.5.0/lib/mongo/socket/ssl.rb:57:in `block in connect!' - /usr/lib/ruby/2.3.0/timeout.rb:101:in `timeout' - /var/lib/gems/2.3.0/gems/mongo-2.5.0/lib/mongo/socket/ssl.rb:56:in `connect!' - /var/lib/gems/2.3.0/gems/mongo-2.5.0/lib/mongo/address.rb:172:in `connect_socket!' - /var/lib/gems/2.3.0/gems/mongo-2.5.0/lib/mongo/server/connection.rb:86:in `connect!' - /var/lib/gems/2.3.0/gems/mongo-2.5.0/lib/mongo/server/connectable.rb:84:in `ensure_connected' - /var/lib/gems/2.3.0/gems/mongo-2.5.0/lib/mongo/server/connection.rb:256:in `write' - -This error was last reported on Ruby Driver version 2.5.1, so updating the driver -can potentially solve this issue. A user has reported that they solved this issue -as follows: - -.. blockquote:: - - DNS servers on EC2 are generated in /etc/resolv.conf by default. Following - the `answer `_ - and setting the nameservers to Google NS I was able to fix this issue. - -You can find more information about this issue here: -`MONGOID-4527 `_. - - -``Mongo::Auth::Unauthorized`` User is Not Authorized to Access -============================================================== - -Some users reported getting the following error: - -.. code-block:: ruby - - Mongo::Auth::Unauthorized: User ... is not authorized to access ... - -After upgrading to Ruby Driver version 2.5+, the driver was changed to use -the scram authorization mechanism by default. This error might happen if the -authorization mechanism your user was created with does not match the -authorization mechanism used by the driver. You can specify the preferred -authorization mechanism using the ``auth_mech`` option in your ``mongoid.yml`` -file. - -See more on this issue here: -`RUBY-1684 `_. - diff --git a/docs/tutorials/quick-start.txt b/docs/tutorials/quick-start.txt deleted file mode 100644 index fbfafc3e69..0000000000 --- a/docs/tutorials/quick-start.txt +++ /dev/null @@ -1,272 +0,0 @@ -*********************** -Ruby Driver Quick Start -*********************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Prerequisites -============= - -- A running MongoDB instance on localhost using the default port, 27017. -- The Ruby MongoDB driver. See :ref:`installation ` - for instructions on how to install the MongoDB driver. -- The following statement at the top of your code: - -.. code-block:: ruby - - require 'mongo' - - -Make a Connection -================= - -Use ``Mongo::Client`` to establish a connection to a running MongoDB -instance. - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test') - -You can also use a URI connection string: - -.. code-block:: ruby - - client = Mongo::Client.new('mongodb://127.0.0.1:27017/test') - -.. seealso:: - :ref:`Connect to a replica set `, - :ref:`Connect to a sharded cluster `, - :ref:`Client options ` - -Access a Database and a Collection -================================== - -The following examples demonstrate how to access a particular database -and show its collections: - -.. code-block:: ruby - - client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test') - db = client.database - - db.collections # returns a list of collection objects - db.collection_names # returns a list of collection names - db.list_collections # returns a list of collection metadata hashes - -To access a collection, refer to it by name. - -.. code-block:: ruby - - collection = client[:restaurants] - -If the collection does not exist, the server will create it the first -time you put data into it. - -Insert a Document -================= - -To insert a single document into a collection, use the -``insert_one`` method. - -.. code-block:: ruby - - client = Mongo::Client.new('mongodb://127.0.0.1:27017/test') - - collection = client[:people] - - doc = { - name: 'Steve', - hobbies: [ 'hiking', 'tennis', 'fly fishing' ], - siblings: { - brothers: 0, - sisters: 1 - } - } - - result = collection.insert_one(doc) - result.n # returns 1, because one document was inserted - -To insert multiple documents into a collection, use the -``insert_many`` method. - -.. code-block:: ruby - - docs = [ { _id: 1, name: 'Steve', - hobbies: [ 'hiking', 'tennis', 'fly fishing' ], - siblings: { brothers: 0, sisters: 1 } }, - { _id: 2, name: 'Sally', - hobbies: ['skiing', 'stamp collecting' ], - siblings: { brothers: 1, sisters: 0 } } ] - - result = collection.insert_many(docs) - result.inserted_count # returns 2 because two documents were inserted - -Query the Collection -==================== - -Use the ``find`` method to create collection queries. - -An empty query filter returns all documents in the collection. - -.. code-block:: ruby - - client = Mongo::Client.new('mongodb://127.0.0.1:27017/test') - collection = client[:people] - - collection.find.each do |document| - #=> Yields a BSON::Document. - end - -Use a query filter to find only matching documents. - -.. code-block:: ruby - - client = Mongo::Client.new('mongodb://127.0.0.1:27017/test') - collection = client[:people] - - puts collection.find( { name: 'Sally' } ).first - -The example should print the following: - -.. code-block:: javascript - - {"_id" => 2, "name" => "Sally", "hobbies" => ["skiing", "stamp collecting"], "siblings" => { "brothers": 1, "sisters": 0 } } - -Query nested documents by specifying the keys and values you want -to match. - -.. code-block:: ruby - - client = Mongo::Client.new('mongodb://127.0.0.1:27017/test') - collection = client[:people] - - puts collection.find("siblings.sisters": 1 ).first - -The example should print the following: - -.. code-block:: javascript - - {"_id"=>1, "name"=>"Steve", "hobbies"=>["hiking", "tennis", "fly fishing"], "siblings"=>{"brothers"=>0, "sisters"=>1}} - -.. seealso:: - - :ref:`Query Options`, :ref:`Read Preference` - -Update Documents -================ - -There are several update methods, including ``update_one`` and -``update_many``. ``update_one`` updates a single document, while -``update_many`` updates multiple documents at once. - -Both methods take as arguments a query filter document and a second -document with the update data. Use ``$set`` to add or update a -particular field or fields. Without ``$set``, the entire existing -document is replaced with the update data. - -.. code-block:: ruby - - client = Mongo::Client.new('mongodb://127.0.0.1:27017/test') - collection = client[:people] - - result = collection.update_one( { 'name' => 'Sally' }, { '$set' => { 'phone_number' => "555-555-5555" } } ) - - puts collection.find( { 'name' => 'Sally' } ).first - -The example should print the following: - -.. code-block:: javascript - - {"_id" => 2, "name" => "Sally", "hobbies" => ["skiing", "stamp collecting"], "phone_number" => "555-555-5555"} - -The following example uses ``update_many`` with a blank query filter -to update all the documents in the collection. - -.. code-block:: ruby - - client = Mongo::Client.new('mongodb://127.0.0.1:27017/test') - collection = client[:people] - - result = collection.update_many( {}, { '$set' => { 'age' => 36 } } ) - - puts result.modified_count # returns 2 because 2 documents were updated - -.. seealso:: - - :ref:`Other update options` - -Delete Documents -================ - -Use the ``delete_one`` or ``delete_many`` methods to delete documents -from a collection (either singly or several at once). - -.. code-block:: ruby - - client = Mongo::Client.new('mongodb://127.0.0.1:27017/test') - collection = client[:people] - - result = collection.delete_one( { name: 'Steve' } ) - - puts result.deleted_count # returns 1 because one document was deleted - -The following example inserts two more records into the collection, -then deletes all the documents with a ``name`` field which -matches a regular expression to find a string which begins with "S". - -.. code-block:: ruby - - client = Mongo::Client.new('mongodb://127.0.0.1:27017/test') - collection = client[:people] - - collection.insert_many([ { _id: 3, name: "Arnold" }, { _id: 4, name: "Susan" } ]) - - puts collection.count # counts all documents in collection - - result = collection.delete_many({ name: /$S*/ }) - - puts result.deleted_count # returns the number of documents deleted - -Create Indexes -============== - -Use the ``create_one`` or ``create_many`` methods to create indexes -singly or several at once. - -.. code-block:: ruby - - client = Mongo::Client.new('mongodb://127.0.0.1:27017/test') - collection = client[:people] - - collection.indexes.create_one({ name: 1 }, unique: true) - -Use the ``create_many`` method to create several indexes with one -statement. Note that when using ``create_many``, the syntax is -different from ``create_one``. - -.. code-block:: ruby - - client = Mongo::Client.new('mongodb://127.0.0.1:27017/test') - collection = client[:people] - - collection.indexes.create_many([ - { key: { name: 1 } , unique: true }, - { key: { hobbies: 1 } }, - ]) - -.. seealso:: - - :ref:`Index options ` - -Complete Sample App -=================== - -A sample app using the Ruby driver for several common use cases -is available for download from -`GitHub `_. diff --git a/gemfiles/standard.rb b/gemfiles/standard.rb index f29b91cb4b..65c628b56b 100644 --- a/gemfiles/standard.rb +++ b/gemfiles/standard.rb @@ -65,8 +65,9 @@ def standard_dependencies gem 'tilt' # solargraph depends on rbs, which won't build on jruby for some reason gem 'solargraph', platforms: :mri + gem 'ruby-lsp', platforms: :mri end - gem 'libmongocrypt-helper', '~> 1.8.0' if ENV['FLE'] == 'helper' + gem 'libmongocrypt-helper', '~> 1.11.0' if ENV['FLE'] == 'helper' end # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/BlockLength diff --git a/lib/mongo.rb b/lib/mongo.rb index b90cd4a011..c866ad1a9e 100644 --- a/lib/mongo.rb +++ b/lib/mongo.rb @@ -39,6 +39,7 @@ require 'mongo/semaphore' require 'mongo/distinguishing_semaphore' require 'mongo/condition_variable' +require 'mongo/csot_timeout_holder' require 'mongo/options' require 'mongo/loggable' require 'mongo/cluster_time' diff --git a/lib/mongo/address.rb b/lib/mongo/address.rb index 90b82dec11..e349ee0619 100644 --- a/lib/mongo/address.rb +++ b/lib/mongo/address.rb @@ -178,6 +178,9 @@ def inspect # @param [ Hash ] opts The options. # # @option opts [ Float ] :connect_timeout Connect timeout. + # @option opts [ Boolean ] :csot Whether the client-side operation timeout + # should be considered when connecting the socket. This option influences + # only what errors will be raised if timeout expires. # @option opts [ true | false ] :ssl Whether to use SSL. # @option opts [ String ] :ssl_ca_cert # Same as the corresponding Client/Socket::SSL option. @@ -214,11 +217,12 @@ def inspect # @since 2.0.0 # @api private def socket(socket_timeout, opts = {}) + csot = !!opts[:csot] opts = { connect_timeout: Server::CONNECT_TIMEOUT, }.update(options).update(Hash[opts.map { |k, v| [k.to_sym, v] }]) - map_exceptions do + map_exceptions(csot) do if seed.downcase =~ Unix::MATCH specific_address = Unix.new(seed.downcase) return specific_address.socket(socket_timeout, opts) @@ -281,11 +285,26 @@ def parse_host_port end end - def map_exceptions + # Maps some errors to different ones, mostly low-level errors to driver + # level errors + # + # @param [ Boolean ] csot Whether the client-side operation timeout + # should be considered when connecting the socket. + def map_exceptions(csot) begin yield rescue Errno::ETIMEDOUT => e - raise Error::SocketTimeoutError, "#{e.class}: #{e} (for #{self})" + if csot + raise Error::TimeoutError, "#{e.class}: #{e} (for #{self})" + else + raise Error::SocketTimeoutError, "#{e.class}: #{e} (for #{self})" + end + rescue Error::SocketTimeoutError => e + if csot + raise Error::TimeoutError, "#{e.class}: #{e} (for #{self})" + else + raise e + end rescue IOError, SystemCallError => e raise Error::SocketError, "#{e.class}: #{e} (for #{self})" rescue OpenSSL::SSL::SSLError => e diff --git a/lib/mongo/auth/aws/credentials_retriever.rb b/lib/mongo/auth/aws/credentials_retriever.rb index fdbbb0e015..7ab83750d5 100644 --- a/lib/mongo/auth/aws/credentials_retriever.rb +++ b/lib/mongo/auth/aws/credentials_retriever.rb @@ -69,20 +69,24 @@ def initialize(user = nil, credentials_cache: CredentialsCache.instance) # Retrieves a valid set of credentials, if possible, or raises # Auth::InvalidConfiguration. # + # @param [ CsotTimeoutHolder | nil ] timeout_holder CSOT timeout, if any. + # # @return [ Auth::Aws::Credentials ] A valid set of credentials. # # @raise Auth::InvalidConfiguration if a source contains an invalid set # of credentials. # @raise Auth::Aws::CredentialsNotFound if credentials could not be # retrieved from any source. - def credentials + # @raise Error::TimeoutError if credentials cannot be retrieved within + # the timeout defined on the operation context. + def credentials(timeout_holder = nil) credentials = credentials_from_user(user) return credentials unless credentials.nil? credentials = credentials_from_environment return credentials unless credentials.nil? - credentials = @credentials_cache.fetch { obtain_credentials_from_endpoints } + credentials = @credentials_cache.fetch { obtain_credentials_from_endpoints(timeout_holder) } return credentials unless credentials.nil? raise Auth::Aws::CredentialsNotFound @@ -127,17 +131,21 @@ def credentials_from_environment # Returns credentials from the AWS metadata endpoints. # + # @param [ CsotTimeoutHolder ] timeout_holder CSOT timeout. + # # @return [ Auth::Aws::Credentials | nil ] A set of credentials, or nil # if retrieval failed or the obtained credentials are invalid. # # @raise Auth::InvalidConfiguration if a source contains an invalid set # of credentials. - def obtain_credentials_from_endpoints - if (credentials = web_identity_credentials) && credentials_valid?(credentials, 'Web identity token') + # @ raise Error::TimeoutError if credentials cannot be retrieved within + # the timeout defined on the operation context. + def obtain_credentials_from_endpoints(timeout_holder = nil) + if (credentials = web_identity_credentials(timeout_holder)) && credentials_valid?(credentials, 'Web identity token') credentials - elsif (credentials = ecs_metadata_credentials) && credentials_valid?(credentials, 'ECS task metadata') + elsif (credentials = ecs_metadata_credentials(timeout_holder)) && credentials_valid?(credentials, 'ECS task metadata') credentials - elsif (credentials = ec2_metadata_credentials) && credentials_valid?(credentials, 'EC2 instance metadata') + elsif (credentials = ec2_metadata_credentials(timeout_holder)) && credentials_valid?(credentials, 'EC2 instance metadata') credentials end end @@ -145,21 +153,26 @@ def obtain_credentials_from_endpoints # Returns credentials from the EC2 metadata endpoint. The credentials # could be empty, partial or invalid. # + # @param [ CsotTimeoutHolder ] timeout_holder CSOT timeout. + # # @return [ Auth::Aws::Credentials | nil ] A set of credentials, or nil # if retrieval failed. - def ec2_metadata_credentials + # @ raise Error::TimeoutError if credentials cannot be retrieved within + # the timeout. + def ec2_metadata_credentials(timeout_holder = nil) + timeout_holder&.check_timeout! http = Net::HTTP.new('169.254.169.254') req = Net::HTTP::Put.new('/latest/api/token', # The TTL is required in order to obtain the metadata token. {'x-aws-ec2-metadata-token-ttl-seconds' => '30'}) - resp = ::Timeout.timeout(METADATA_TIMEOUT) do + resp = with_timeout(timeout_holder) do http.request(req) end if resp.code != '200' return nil end metadata_token = resp.body - resp = ::Timeout.timeout(METADATA_TIMEOUT) do + resp = with_timeout(timeout_holder) do http_get(http, '/latest/meta-data/iam/security-credentials', metadata_token) end if resp.code != '200' @@ -167,7 +180,7 @@ def ec2_metadata_credentials end role_name = resp.body escaped_role_name = CGI.escape(role_name).gsub('+', '%20') - resp = ::Timeout.timeout(METADATA_TIMEOUT) do + resp = with_timeout(timeout_holder) do http_get(http, "/latest/meta-data/iam/security-credentials/#{escaped_role_name}", metadata_token) end if resp.code != '200' @@ -189,7 +202,17 @@ def ec2_metadata_credentials return nil end - def ecs_metadata_credentials + # Returns credentials from the ECS metadata endpoint. The credentials + # could be empty, partial or invalid. + # + # @param [ CsotTimeoutHolder | nil ] timeout_holder CSOT timeout. + # + # @return [ Auth::Aws::Credentials | nil ] A set of credentials, or nil + # if retrieval failed. + # @ raise Error::TimeoutError if credentials cannot be retrieved within + # the timeout defined on the operation context. + def ecs_metadata_credentials(timeout_holder = nil) + timeout_holder&.check_timeout! relative_uri = ENV['AWS_CONTAINER_CREDENTIALS_RELATIVE_URI'] if relative_uri.nil? || relative_uri.empty? return nil @@ -203,7 +226,7 @@ def ecs_metadata_credentials # a leading slash must be added by the driver, but this is not # in fact needed. req = Net::HTTP::Get.new(relative_uri) - resp = ::Timeout.timeout(METADATA_TIMEOUT) do + resp = with_timeout(timeout_holder) do http.request(req) end if resp.code != '200' @@ -225,13 +248,15 @@ def ecs_metadata_credentials # inside EKS. See https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html # for further details. # + # @param [ CsotTimeoutHolder | nil ] timeout_holder CSOT timeout. + # # @return [ Auth::Aws::Credentials | nil ] A set of credentials, or nil # if retrieval failed. - def web_identity_credentials + def web_identity_credentials(timeout_holder = nil) web_identity_token, role_arn, role_session_name = prepare_web_identity_inputs return nil if web_identity_token.nil? response = request_web_identity_credentials( - web_identity_token, role_arn, role_session_name + web_identity_token, role_arn, role_session_name, timeout_holder ) return if response.nil? credentials_from_web_identity_response(response) @@ -266,10 +291,15 @@ def prepare_web_identity_inputs # that the caller is assuming. # @param [ String ] role_session_name An identifier for the assumed # role session. + # @param [ CsotTimeoutHolder | nil ] timeout_holder CSOT timeout. # # @return [ Net::HTTPResponse | nil ] AWS API response if successful, # otherwise nil. - def request_web_identity_credentials(token, role_arn, role_session_name) + # + # @ raise Error::TimeoutError if credentials cannot be retrieved within + # the timeout defined on the operation context. + def request_web_identity_credentials(token, role_arn, role_session_name, timeout_holder) + timeout_holder&.check_timeout! uri = URI('https://sts.amazonaws.com/') params = { 'Action' => 'AssumeRoleWithWebIdentity', @@ -281,8 +311,10 @@ def request_web_identity_credentials(token, role_arn, role_session_name) uri.query = ::URI.encode_www_form(params) req = Net::HTTP::Post.new(uri) req['Accept'] = 'application/json' - resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |https| - https.request(req) + resp = with_timeout(timeout_holder) do + Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |https| + https.request(req) + end end if resp.code != '200' return nil @@ -351,6 +383,27 @@ def credentials_valid?(credentials, source) true end + + # Execute the given block considering the timeout defined on the context, + # or the default timeout value. + # + # We use +Timeout.timeout+ here because there is no other acceptable easy + # way to time limit http requests. + # + # @param [ CsotTimeoutHolder | nil ] timeout_holder CSOT timeout. + # + # @ raise Error::TimeoutError if deadline exceeded. + def with_timeout(timeout_holder) + timeout = timeout_holder&.remaining_timeout_sec! || METADATA_TIMEOUT + exception_class = if timeout_holder&.csot? + Error::TimeoutError + else + nil + end + ::Timeout.timeout(timeout, exception_class) do + yield + end + end end end end diff --git a/lib/mongo/auth/base.rb b/lib/mongo/auth/base.rb index efcae3c151..7c49e96527 100644 --- a/lib/mongo/auth/base.rb +++ b/lib/mongo/auth/base.rb @@ -117,7 +117,7 @@ def dispatch_msg(connection, conversation, msg) else nil end - result = Operation::Result.new(reply, connection.description, connection_global_id) + result = Operation::Result.new(reply, connection.description, connection_global_id, context: context) connection.update_cluster_time(result) reply_document end diff --git a/lib/mongo/bulk_write.rb b/lib/mongo/bulk_write.rb index 336a98396e..1286e39634 100644 --- a/lib/mongo/bulk_write.rb +++ b/lib/mongo/bulk_write.rb @@ -60,10 +60,15 @@ def execute result_combiner = ResultCombiner.new operations = op_combiner.combine validate_requests! + deadline = calculate_deadline - client.send(:with_session, @options) do |session| - context = Operation::Context.new(client: client, session: session) + client.with_session(@options) do |session| operations.each do |operation| + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: { operation_timeout_ms: op_timeout_ms(deadline) } + ) if single_statement?(operation) write_concern = write_concern(session) write_with_retry(write_concern, context: context) do |connection, txn_num, context| @@ -124,6 +129,9 @@ def initialize(collection, requests, options = {}) @collection = collection @requests = requests @options = options || {} + if @options[:timeout_ms] && @options[:timeout_ms] < 0 + raise ArgumentError, "timeout_ms options must be non-negative integer" + end end # Is the bulk write ordered? @@ -162,6 +170,31 @@ def write_concern(session = nil) :update_one, :insert_one ].freeze + # @return [ Float | nil ] Deadline for the batch of operations, if set. + def calculate_deadline + timeout_ms = @options[:timeout_ms] || collection.timeout_ms + return nil if timeout_ms.nil? + + if timeout_ms == 0 + 0 + else + Utils.monotonic_time + (timeout_ms / 1_000.0) + end + end + + # @param [ Float | nil ] deadline Deadline for the batch of operations. + # + # @return [ Integer | nil ] Timeout in milliseconds for the next operation. + def op_timeout_ms(deadline) + return nil if deadline.nil? + + if deadline == 0 + 0 + else + ((deadline - Utils.monotonic_time) * 1_000).to_i + end + end + def single_statement?(operation) SINGLE_STATEMENT_OPS.include?(operation.keys.first) end diff --git a/lib/mongo/client.rb b/lib/mongo/client.rb index e06231d4ad..a5d3e030b4 100644 --- a/lib/mongo/client.rb +++ b/lib/mongo/client.rb @@ -111,6 +111,7 @@ class Client :ssl_verify_certificate, :ssl_verify_hostname, :ssl_verify_ocsp_endpoint, + :timeout_ms, :truncate_logs, :user, :wait_queue_timeout, @@ -349,7 +350,8 @@ def hash # @option options [ Integer ] :server_selection_timeout The timeout in seconds # for selecting a server for an operation. # @option options [ Float ] :socket_timeout The timeout, in seconds, to - # execute operations on a socket. + # execute operations on a socket. This option is deprecated, use + # :timeout_ms instead. # @option options [ Integer ] :srv_max_hosts The maximum number of mongoses # that the driver will communicate with for sharded topologies. If this # option is 0, then there will be no maximum number of mongoses. If the @@ -413,11 +415,15 @@ def hash # @option options [ true, false ] :ssl_verify_hostname Whether to perform peer hostname # validation. This setting overrides :ssl_verify with respect to whether hostname validation # is performed. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the feature is not enabled. # @option options [ true, false ] :truncate_logs Whether to truncate the # logs at the default 250 characters. # @option options [ String ] :user The user name. # @option options [ Float ] :wait_queue_timeout The time to wait, in # seconds, in the connection pool for a connection to be checked in. + # This option is deprecated, use :timeout_ms instead. # @option options [ Array ] :wrapping_libraries Information about # libraries such as ODMs that are wrapping the driver, to be added to # metadata sent to the server. Specify the lower level libraries first. @@ -425,7 +431,7 @@ def hash # @option options [ Hash ] :write Deprecated. Equivalent to :write_concern # option. # @option options [ Hash ] :write_concern The write concern options. - # Can be :w => Integer|String, :wtimeout => Integer (in milliseconds), + # Can be :w => Integer|String, :wtimeout => Integer (in milliseconds, deprecated), # :j => Boolean, :fsync => Boolean. # @option options [ Integer ] :zlib_compression_level The Zlib compression level to use, if using compression. # See Ruby's Zlib module for valid levels. @@ -934,8 +940,11 @@ def reconnect # See https://mongodb.com/docs/manual/reference/command/listDatabases/ # for more information and usage. # @option opts [ Session ] :session The session to use. - # @option options [ Object ] :comment A user-provided + # @option opts [ Object ] :comment A user-provided # comment to attach to this command. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the feature is not enabled. # # @return [ Array ] The names of the databases. # @@ -955,7 +964,12 @@ def database_names(filter = {}, opts = {}) # # @option opts [ true, false ] :authorized_databases A flag that determines # which databases are returned based on user privileges when access control - # is enabled + # is enabled. + # @option opts [ Object ] :comment A user-provided + # comment to attach to this command. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the feature is not enabled. # # See https://mongodb.com/docs/manual/reference/command/listDatabases/ # for more information and usage. @@ -1095,7 +1109,7 @@ def watch(pipeline = [], options = {}) return use(Database::ADMIN).watch(pipeline, options) unless database.name == Database::ADMIN view_options = options.dup - view_options[:await_data] = true if options[:max_await_time_ms] + view_options[:cursor_type] = :tailable_await if options[:max_await_time_ms] Mongo::Collection::View::ChangeStream.new( Mongo::Collection::View.new(self["#{Database::COMMAND}.aggregate"], {}, view_options), @@ -1185,6 +1199,22 @@ def encrypted_fields_map @encrypted_fields_map ||= @options.fetch(:auto_encryption_options, {})[:encrypted_fields_map] end + # @return [ Integer | nil ] Value of timeout_ms option if set. + # @api private + def timeout_ms + @options[:timeout_ms] + end + + # @return [ Float | nil ] Value of timeout_ms option converted to seconds. + # @api private + def timeout_sec + if timeout_ms.nil? + nil + else + timeout_ms / 1_000.0 + end + end + private # Create a new encrypter object using the client's auto encryption options @@ -1230,6 +1260,8 @@ def do_close # @option options [ true | false ] :implicit When no session is passed in, # whether to create an implicit session. # @option options [ Session ] :session The session to validate and return. + # @option options [ Operation::Context | nil ] :context Context of the + # operation the session is used for. # # @return [ Session ] A session object. # @@ -1242,7 +1274,7 @@ def get_session!(options = {}) return options[:session].validate!(self) end - cluster.validate_session_support! + cluster.validate_session_support!(timeout: timeout_sec) options = {implicit: true}.update(options) diff --git a/lib/mongo/client_encryption.rb b/lib/mongo/client_encryption.rb index 5390048edd..05bd1f6f8c 100644 --- a/lib/mongo/client_encryption.rb +++ b/lib/mongo/client_encryption.rb @@ -40,6 +40,9 @@ class ClientEncryption # should be hashes of TLS connection options. The options are equivalent # to TLS connection options of Mongo::Client. # @see Mongo::Client#initialize for list of TLS options. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the feature is disabled. # # @raise [ ArgumentError ] If required options are missing or incorrectly # formatted. @@ -131,7 +134,7 @@ def encrypt(value, options={}) # {'$and' => [{'$gt' => ['$field', 10]}, {'$lt' => ['$field', 20]}} # ) # {$and: [{$gt: [, ]}, {$lt: [, ]}] - # Only supported when queryType is "rangePreview" and algorithm is "RangePreview". + # Only supported when queryType is "range" and algorithm is "Range". # @note: The Range algorithm is experimental only. It is not intended # for public use. It is subject to breaking changes. # @@ -143,11 +146,11 @@ def encrypt(value, options={}) # @option options [ String ] :key_alt_name The alternate name for the # encryption key. # @option options [ String ] :algorithm The algorithm used to encrypt the - # expression. The only allowed value is "RangePreview" + # expression. The only allowed value is "Range" # @option options [ Integer | nil ] :contention_factor Contention factor # to be applied If not provided, it defaults to a value of 0. # @option options [ String | nil ] query_type Query type to be applied. - # The only allowed value is "rangePreview". + # The only allowed value is "range". # # @note The :key_id and :key_alt_name options are mutually exclusive. Only # one is required to perform explicit encryption. diff --git a/lib/mongo/cluster.rb b/lib/mongo/cluster.rb index eac6d6229d..46e9f556f1 100644 --- a/lib/mongo/cluster.rb +++ b/lib/mongo/cluster.rb @@ -779,12 +779,19 @@ def has_writable_server? # Deprecated and ignored. # @param [ Session | nil ] session Optional session to take into account # for mongos pinning. + # @param [ Float | nil ] :timeout Timeout in seconds for the operation, + # if any. # # @return [ Mongo::Server ] A primary server. # # @since 2.0.0 - def next_primary(ping = nil, session = nil) - ServerSelector.primary.select_server(self, nil, session) + def next_primary(ping = nil, session = nil, timeout: nil) + ServerSelector.primary.select_server( + self, + nil, + session, + timeout: timeout + ) end # Get the connection pool for the server. @@ -974,8 +981,11 @@ def disconnect_server_if_connected(server) # any servers and doesn't find any servers for the duration of # server selection timeout. # + # @param [ Float | nil ] :timeout Timeout for the validation. Since the + # validation process involves server selection, + # # @api private - def validate_session_support! + def validate_session_support!(timeout: nil) if topology.is_a?(Topology::LoadBalanced) return end @@ -993,7 +1003,7 @@ def validate_session_support! # No data bearing servers known - perform server selection to try to # get a response from at least one of them, to return an accurate # assessment of whether sessions are currently supported. - ServerSelector.get(mode: :primary_preferred).select_server(self) + ServerSelector.get(mode: :primary_preferred).select_server(self, timeout: timeout) @state_change_lock.synchronize do @sdam_flow_lock.synchronize do unless topology.logical_session_timeout diff --git a/lib/mongo/cluster/reapers/cursor_reaper.rb b/lib/mongo/cluster/reapers/cursor_reaper.rb index b08bab2017..beaf660c41 100644 --- a/lib/mongo/cluster/reapers/cursor_reaper.rb +++ b/lib/mongo/cluster/reapers/cursor_reaper.rb @@ -194,7 +194,12 @@ def kill_cursors server_api: server.options[:server_api], connection_global_id: kill_spec.connection_global_id, } - op.execute(server, context: Operation::Context.new(options: options)) + if connection = kill_spec.connection + op.execute_with_connection(connection, context: Operation::Context.new(options: options)) + connection.connection_pool.check_in(connection) + else + op.execute(server, context: Operation::Context.new(options: options)) + end if session = kill_spec.session if session.implicit? diff --git a/lib/mongo/cluster/sdam_flow.rb b/lib/mongo/cluster/sdam_flow.rb index eab2fd88b9..bd908b682e 100644 --- a/lib/mongo/cluster/sdam_flow.rb +++ b/lib/mongo/cluster/sdam_flow.rb @@ -116,8 +116,12 @@ def server_description_changed log_warn( "Server #{updated_desc.address.to_s} has an incorrect replica set name '#{updated_desc.replica_set_name}'; expected '#{topology.replica_set_name}'" ) - @updated_desc = ::Mongo::Server::Description.new(updated_desc.address, - {}, average_round_trip_time: updated_desc.average_round_trip_time) + @updated_desc = ::Mongo::Server::Description.new( + updated_desc.address, + {}, + average_round_trip_time: updated_desc.average_round_trip_time, + minimum_round_trip_time: updated_desc.minimum_round_trip_time + ) update_server_descriptions end end @@ -233,8 +237,12 @@ def update_rs_from_primary end if stale_primary? - @updated_desc = ::Mongo::Server::Description.new(updated_desc.address, - {}, average_round_trip_time: updated_desc.average_round_trip_time) + @updated_desc = ::Mongo::Server::Description.new( + updated_desc.address, + {}, + average_round_trip_time: updated_desc.average_round_trip_time, + minimum_round_trip_time: updated_desc.minimum_round_trip_time + ) update_server_descriptions check_if_has_primary return @@ -270,9 +278,14 @@ def update_rs_from_primary servers_list.each do |server| if server.address != updated_desc.address if server.primary? - server.update_description(::Mongo::Server::Description.new( - server.address, {}, - average_round_trip_time: server.description.average_round_trip_time)) + server.update_description( + ::Mongo::Server::Description.new( + server.address, + {}, + average_round_trip_time: server.description.average_round_trip_time, + minimum_round_trip_time: updated_desc.minimum_round_trip_time + ) + ) end end end diff --git a/lib/mongo/collection.rb b/lib/mongo/collection.rb index a2b2076b7d..b9cbefee0c 100644 --- a/lib/mongo/collection.rb +++ b/lib/mongo/collection.rb @@ -134,8 +134,12 @@ def ==(other) # and *:nearest*. # - *:tag_sets* -- an array of hashes. # - *:local_threshold*. - # @option opts [ Session ] :session The session to use for the operation. - # @option opts [ Integer ] :size The size of the capped collection. + # @option options [ Session ] :session The session to use for the operation. + # @option options [ Integer ] :size The size of the capped collection. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the database or the client. # @option opts [ Hash ] :time_series Create a time-series collection. # The hash may have the following items: # - *:timeField* -- The name of the field which contains the date in each @@ -163,6 +167,7 @@ def initialize(database, name, options = {}) @database = database @name = name.to_s.freeze @options = options.dup + @timeout_ms = options.delete(:timeout_ms) =begin WriteConcern object support if @options[:write_concern].is_a?(WriteConcern::Base) # Cache the instance so that we do not needlessly reconstruct it. @@ -401,7 +406,10 @@ def create(opts = {}) self.write_concern end - context = Operation::Context.new(client: client, session: session) + context = Operation::Context.new( + client: client, + session: session + ) maybe_create_qe_collections(opts[:encrypted_fields], client, session) do |encrypted_fields| Operation::Create.new( selector: operation, @@ -413,7 +421,10 @@ def create(opts = {}) collation: options[:collation] || options['collation'], encrypted_fields: encrypted_fields, validator: options[:validator], - ).execute(next_primary(nil, session), context: context) + ).execute( + next_primary(nil, session), + context: context + ) end end end @@ -432,12 +443,16 @@ def create(opts = {}) # @option opts [ Hash ] :write_concern The write concern options. # @option opts [ Hash | nil ] :encrypted_fields Encrypted fields hash that # was provided to `create` collection helper. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @return [ Result ] The result of the command. # # @since 2.0.0 def drop(opts = {}) - client.send(:with_session, opts) do |session| + client.with_session(opts) do |session| maybe_drop_emm_collections(opts[:encrypted_fields], client, session) do temp_write_concern = write_concern write_concern = if opts[:write_concern] @@ -445,7 +460,11 @@ def drop(opts = {}) else temp_write_concern end - context = Operation::Context.new(client: client, session: session) + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(opts) + ) operation = Operation::Drop.new({ selector: { :drop => name }, db_name: database.name, @@ -481,8 +500,9 @@ def drop(opts = {}) # this command. # @option options [ :tailable, :tailable_await ] :cursor_type The type of cursor to use. # @option options [ Integer ] :limit The max number of docs to return from the query. - # @option options [ Integer ] :max_time_ms - # The maximum amount of time to allow the query to run, in milliseconds. + # @option options [ Integer ] :max_time_ms The maximum amount of time to + # allow the query to run, in milliseconds. This option is deprecated, use + # :timeout_ms instead. # @option options [ Hash ] :modifiers A document containing meta-operators modifying the # output or behavior of a query. # @option options [ true | false ] :no_cursor_timeout The server normally times out idle @@ -496,6 +516,13 @@ def drop(opts = {}) # @option options [ Integer ] :skip The number of docs to skip before returning results. # @option options [ Hash ] :sort The key and direction pairs by which the result set # will be sorted. + # @option options [ :cursor_lifetime | :iteration ] :timeout_mode How to interpret + # :timeout_ms (whether it applies to the lifetime of the cursor, or per + # iteration). + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option options [ Hash ] :let Mapping of variables to use in the command. # See the server documentation for details. # @@ -526,13 +553,14 @@ def find(filter = nil, options = {}) # @option options [ String ] :hint The index to use for the aggregation. # @option options [ Hash ] :let Mapping of variables to use in the pipeline. # See the server documentation for details. - # @option options [ Integer ] :max_time_ms The maximum amount of time in - # milliseconds to allow the aggregation to run. - # @option options [ true | false ] :use_cursor Indicates whether the command - # will request that the server provide results using a cursor. Note that - # as of server version 3.6, aggregations always provide results using a - # cursor and this option is therefore not valid. + # @option options [ Integer ] :max_time_ms The maximum amount of time to + # allow the query to run, in milliseconds. This option is deprecated, use + # :timeout_ms instead. # @option options [ Session ] :session The session to use. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @return [ View::Aggregation ] The aggregation object. # @@ -600,6 +628,13 @@ def aggregate(pipeline, options = {}) # events included with this flag set are: createIndexes, dropIndexes, # modify, create, shardCollection, reshardCollection, # refineCollectionShardKey. + # @option options [ :cursor_lifetime | :iteration ] :timeout_mode How to interpret + # :timeout_ms (whether it applies to the lifetime of the cursor, or per + # iteration). + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @note A change stream only allows 'majority' read concern. # @note This helper method is preferable to running a raw aggregation with @@ -610,7 +645,7 @@ def aggregate(pipeline, options = {}) # @since 2.5.0 def watch(pipeline = [], options = {}) view_options = options.dup - view_options[:await_data] = true if options[:max_await_time_ms] + view_options[:cursor_type] = :tailable_await if options[:max_await_time_ms] View::ChangeStream.new(View.new(self, {}, view_options), pipeline, nil, options) end @@ -624,13 +659,19 @@ def watch(pipeline = [], options = {}) # # @option options [ Hash ] :hint The index to use. # @option options [ Integer ] :limit The maximum number of documents to count. - # @option options [ Integer ] :max_time_ms The maximum amount of time to allow the command to run. + # @option options [ Integer ] :max_time_ms The maximum amount of time to + # allow the query to run, in milliseconds. This option is deprecated, use + # :timeout_ms instead. # @option options [ Integer ] :skip The number of documents to skip before counting. # @option options [ Hash ] :read The read preference options. # @option options [ Hash ] :collation The collation to use. # @option options [ Session ] :session The session to use. # @option options [ Object ] :comment A user-provided # comment to attach to this command. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @return [ Integer ] The document count. # @@ -667,6 +708,10 @@ def count(filter = nil, options = {}) # @option options [ Session ] :session The session to use. # @option options [ Object ] :comment A user-provided # comment to attach to this command. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @return [ Integer ] The document count. # @@ -688,6 +733,10 @@ def count_documents(filter = {}, options = {}) # @option options [ Hash ] :read The read preference options. # @option options [ Object ] :comment A user-provided # comment to attach to this command. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @return [ Integer ] The document count. # @@ -705,10 +754,16 @@ def estimated_document_count(options = {}) # @param [ Hash ] filter The documents from which to retrieve the distinct values. # @param [ Hash ] options The distinct command options. # - # @option options [ Integer ] :max_time_ms The maximum amount of time to allow the command to run. + # @option options [ Integer ] :max_time_ms The maximum amount of time to + # allow the query to run, in milliseconds. This option is deprecated, use + # :timeout_ms instead. # @option options [ Hash ] :read The read preference options. # @option options [ Hash ] :collation The collation to use. # @option options [ Session ] :session The session to use. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @return [ Array ] The list of distinct values. # @@ -781,6 +836,10 @@ def inspect # @option opts [ Object ] :comment A user-provided comment to attach to # this command. # @option opts [ Session ] :session The session to use for the operation. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option opts [ Hash ] :write_concern The write concern options. # Can be :w => Integer, :fsync => Boolean, :j => Boolean. # @@ -801,7 +860,11 @@ def insert_one(document, opts = {}) raise ArgumentError, "Document to be inserted cannot be nil" end - context = Operation::Context.new(client: client, session: session) + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(opts) + ) write_with_retry(write_concern, context: context) do |connection, txn_num, context| Operation::Insert.new( :documents => [ document ], @@ -834,6 +897,10 @@ def insert_one(document, opts = {}) # @option options [ true | false ] :ordered Whether the operations # should be executed in order. # @option options [ Session ] :session The session to use for the operation. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option options [ Hash ] :write_concern The write concern options. # Can be :w => Integer, :fsync => Boolean, :j => Boolean. # @@ -862,6 +929,10 @@ def insert_many(documents, options = {}) # @option options [ true | false ] :bypass_document_validation Whether or # not to skip document level validation. # @option options [ Session ] :session The session to use for the set of operations. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option options [ Hash ] :let Mapping of variables to use in the command. # See the server documentation for details. # @@ -884,6 +955,10 @@ def bulk_write(requests, options = {}) # @option options [ Session ] :session The session to use. # @option options [ Hash | String ] :hint The index to use for this operation. # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option options [ Hash ] :let Mapping of variables to use in the command. # See the server documentation for details. # @@ -906,6 +981,10 @@ def delete_one(filter = nil, options = {}) # @option options [ Session ] :session The session to use. # @option options [ Hash | String ] :hint The index to use for this operation. # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option options [ Hash ] :let Mapping of variables to use in the command. # See the server documentation for details. # @@ -928,15 +1007,23 @@ def delete_many(filter = nil, options = {}) # @param [ Integer ] cursor_count The max number of cursors to return. # @param [ Hash ] options The parallel scan command options. # - # @option options [ Integer ] :max_time_ms The maximum amount of time to allow the command - # to run in milliseconds. + # @option options [ Integer ] :max_time_ms The maximum amount of time to + # allow the query to run, in milliseconds. This option is deprecated, use + # :timeout_ms instead. # @option options [ Session ] :session The session to use. + # @option options [ :cursor_lifetime | :iteration ] :timeout_mode How to interpret + # :timeout_ms (whether it applies to the lifetime of the cursor, or per + # iteration). + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @return [ Array ] An array of cursors. # # @since 2.1 def parallel_scan(cursor_count, options = {}) - find({}, options).send(:parallel_scan, cursor_count, options) + find({}, options).parallel_scan(cursor_count, options) end # Replaces a single document in the collection with the new document. @@ -954,6 +1041,10 @@ def parallel_scan(cursor_count, options = {}) # not to skip document level validation. # @option options [ Hash ] :collation The collation to use. # @option options [ Session ] :session The session to use. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option options [ Hash | String ] :hint The index to use for this operation. # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). # @option options [ Hash ] :let Mapping of variables to use in the command. @@ -983,6 +1074,10 @@ def replace_one(filter, replacement, options = {}) # @option options [ Array ] :array_filters A set of filters specifying to which array elements # an update should apply. # @option options [ Session ] :session The session to use. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option options [ Hash | String ] :hint The index to use for this operation. # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). # @option options [ Hash ] :let Mapping of variables to use in the command. @@ -1012,6 +1107,10 @@ def update_many(filter, update, options = {}) # @option options [ Array ] :array_filters A set of filters specifying to which array elements # an update should apply. # @option options [ Session ] :session The session to use. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option options [ Hash | String ] :hint The index to use for this operation. # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). # @option options [ Hash ] :let Mapping of variables to use in the command. @@ -1033,8 +1132,9 @@ def update_one(filter, update, options = {}) # @param [ Hash ] filter The filter to use. # @param [ Hash ] options The options. # - # @option options [ Integer ] :max_time_ms The maximum amount of time to allow the command - # to run in milliseconds. + # @option options [ Integer ] :max_time_ms The maximum amount of time to + # allow the query to run, in milliseconds. This option is deprecated, use + # :timeout_ms instead. # @option options [ Hash ] :projection The fields to include or exclude in the returned doc. # @option options [ Hash ] :sort The key and direction pairs by which the result set # will be sorted. @@ -1042,6 +1142,10 @@ def update_one(filter, update, options = {}) # Defaults to the collection's write concern. # @option options [ Hash ] :collation The collation to use. # @option options [ Session ] :session The session to use. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option options [ Hash | String ] :hint The index to use for this operation. # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). # @option options [ Hash ] :let Mapping of variables to use in the command. @@ -1086,6 +1190,10 @@ def find_one_and_delete(filter, options = {}) # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). # @option options [ Hash ] :let Mapping of variables to use in the command. # See the server documentation for details. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @return [ BSON::Document ] The document. # @@ -1122,6 +1230,10 @@ def find_one_and_update(filter, update, options = {}) # @option options [ Session ] :session The session to use. # @option options [ Hash | String ] :hint The index to use for this operation. # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option options [ Hash ] :let Mapping of variables to use in the command. # See the server documentation for details. # @@ -1152,5 +1264,28 @@ def namespace def system_collection? name.start_with?('system.') end + + # @return [ Integer | nil ] Operation timeout that is for this database or + # for the corresponding client. + # + # @api private + def timeout_ms + @timeout_ms || database.timeout_ms + end + + # @return [ Hash ] timeout_ms value set on the operation level (if any), + # and/or timeout_ms that is set on collection/database/client level (if any). + # + # @api private + def operation_timeouts(opts = {}) + # TODO: We should re-evaluate if we need two timeouts separately. + {}.tap do |result| + if opts[:timeout_ms].nil? + result[:inherited_timeout_ms] = timeout_ms + else + result[:operation_timeout_ms] = opts.delete(:timeout_ms) + end + end + end end end diff --git a/lib/mongo/collection/helpers.rb b/lib/mongo/collection/helpers.rb index a6feaf9c99..f21dfedc87 100644 --- a/lib/mongo/collection/helpers.rb +++ b/lib/mongo/collection/helpers.rb @@ -30,7 +30,7 @@ module Helpers # @return [ Result ] The result of the execution. def do_drop(operation, session, context) operation.execute(next_primary(nil, session), context: context) - rescue Error::OperationFailure => ex + rescue Error::OperationFailure::Family => ex # NamespaceNotFound if ex.code == 26 || ex.code.nil? && ex.message =~ /ns not found/ false diff --git a/lib/mongo/collection/view.rb b/lib/mongo/collection/view.rb index dcc0f89752..fc33d85b75 100644 --- a/lib/mongo/collection/view.rb +++ b/lib/mongo/collection/view.rb @@ -63,10 +63,10 @@ class View :client, :cluster, :database, + :nro_write_with_retry, :read_with_retry, :read_with_retry_cursor, :write_with_retry, - :nro_write_with_retry, :write_concern_with_session # Delegate to the cluster for the next primary. @@ -74,6 +74,12 @@ class View alias :selector :filter + # @return [ Integer | nil | The timeout_ms value that was passed as an + # option to the view. + # + # @api private + attr_reader :operation_timeout_ms + # Compare two +View+ objects. # # @example Compare the view with another object. @@ -151,15 +157,26 @@ def hash # document more than once. Deprecated as of MongoDB server version 4.0. # @option options [ Hash ] :sort The key and direction pairs used to sort # the results. + # @option options [ :cursor_lifetime | :iteration ] :timeout_mode How to interpret + # :timeout_ms (whether it applies to the lifetime of the cursor, or per + # iteration). + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @since 2.0.0 def initialize(collection, filter = {}, options = {}) validate_doc!(filter) - @collection = collection filter = BSON::Document.new(filter) options = BSON::Document.new(options) + @collection = collection + @operation_timeout_ms = options.delete(:timeout_ms) + + validate_timeout_mode!(options) + # This is when users pass $query in filter and other modifiers # alongside? query = filter.delete(:$query) @@ -171,6 +188,14 @@ def initialize(collection, filter = {}, options = {}) @options = Operation::Find::Builder::Modifiers.map_driver_options(modifiers).merge!(options).freeze end + # The timeout_ms value to use for this operation; either specified as an + # option to the view, or inherited from the collection. + # + # @return [ Integer | nil ] the timeout_ms for this operation + def timeout_ms + operation_timeout_ms || collection.timeout_ms + end + # Get a human-readable string representation of +View+. # # @example Get the inspection. @@ -196,6 +221,20 @@ def write_concern WriteConcern.get(options[:write_concern] || options[:write] || collection.write_concern) end + # @return [ Hash ] timeout_ms value set on the operation level (if any), + # and/or timeout_ms that is set on collection/database/client level (if any). + # + # @api private + def operation_timeouts(opts = {}) + {}.tap do |result| + if opts[:timeout_ms] || operation_timeout_ms + result[:operation_timeout_ms] = opts[:timeout_ms] || operation_timeout_ms + else + result[:inherited_timeout_ms] = collection.timeout_ms + end + end + end + private def initialize_copy(other) @@ -205,13 +244,14 @@ def initialize_copy(other) end def new(options) + options = options.merge(timeout_ms: operation_timeout_ms) if operation_timeout_ms View.new(collection, filter, options) end def view; self; end def with_session(opts = {}, &block) - client.send(:with_session, @options.merge(opts), &block) + client.with_session(@options.merge(opts), &block) end end end diff --git a/lib/mongo/collection/view/aggregation.rb b/lib/mongo/collection/view/aggregation.rb index 396c6eb142..154b50488a 100644 --- a/lib/mongo/collection/view/aggregation.rb +++ b/lib/mongo/collection/view/aggregation.rb @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'mongo/collection/view/aggregation/behavior' + module Mongo class Collection class View @@ -23,46 +25,11 @@ class View # # @since 2.0.0 class Aggregation - extend Forwardable - include Enumerable - include Immutable - include Iterable - include Explainable - include Loggable - include Retryable + include Behavior - # @return [ View ] view The collection view. - attr_reader :view # @return [ Array ] pipeline The aggregation pipeline. attr_reader :pipeline - # Delegate necessary operations to the view. - def_delegators :view, :collection, :read, :cluster - - # Delegate necessary operations to the collection. - def_delegators :collection, :database, :client - - # The reroute message. - # - # @since 2.1.0 - # @deprecated - REROUTE = 'Rerouting the Aggregation operation to the primary server.'.freeze - - # Set to true if disk usage is allowed during the aggregation. - # - # @example Set disk usage flag. - # aggregation.allow_disk_use(true) - # - # @param [ true, false ] value The flag value. - # - # @return [ true, false, Aggregation ] The aggregation if a value was - # set or the value if used as a getter. - # - # @since 2.0.0 - def allow_disk_use(value = nil) - configure(:allow_disk_use, value) - end - # Initialize the aggregation for the provided collection view, pipeline # and options. # @@ -86,59 +53,29 @@ def allow_disk_use(value = nil) # @option options [ Hash ] :let Mapping of variables to use in the pipeline. # See the server documentation for details. # @option options [ Integer ] :max_time_ms The maximum amount of time in - # milliseconds to allow the aggregation to run. - # @option options [ true, false ] :use_cursor Indicates whether the command - # will request that the server provide results using a cursor. Note that - # as of server version 3.6, aggregations always provide results using a - # cursor and this option is therefore not valid. + # milliseconds to allow the aggregation to run. This option is deprecated, use + # :timeout_ms instead. # @option options [ Session ] :session The session to use. + # @option options [ :cursor_lifetime | :iteration ] :timeout_mode How to interpret + # :timeout_ms (whether it applies to the lifetime of the cursor, or per + # iteration). + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @since 2.0.0 def initialize(view, pipeline, options = {}) - @view = view - @pipeline = pipeline.dup - unless Mongo.broken_view_aggregate || view.filter.empty? - @pipeline.unshift(:$match => view.filter) + perform_setup(view, options) do + @pipeline = pipeline.dup + unless Mongo.broken_view_aggregate || view.filter.empty? + @pipeline.unshift(:$match => view.filter) + end end - @options = BSON::Document.new(options).freeze - end - - # Get the explain plan for the aggregation. - # - # @example Get the explain plan for the aggregation. - # aggregation.explain - # - # @return [ Hash ] The explain plan. - # - # @since 2.0.0 - def explain - self.class.new(view, pipeline, options.merge(explain: true)).first - end - - # Whether this aggregation will write its result to a database collection. - # - # @return [ Boolean ] Whether the aggregation will write its result - # to a collection. - # - # @api private - def write? - pipeline.any? { |op| op.key?('$out') || op.key?(:$out) || op.key?('$merge') || op.key?(:$merge) } end private - def server_selector - @view.send(:server_selector) - end - - def aggregate_spec(session, read_preference) - Builder::Aggregation.new( - pipeline, - view, - options.merge(session: session, read_preference: read_preference) - ).specification - end - def new(options) Aggregation.new(view, pipeline, options) end @@ -180,32 +117,29 @@ def effective_read_preference(connection) end - def send_initial_query(server, session) - server.with_connection do |connection| + def send_initial_query(server, context) + if server.load_balancer? + # Connection will be checked in when cursor is drained. + connection = server.pool.check_out(context: context) initial_query_op( - session, + context.session, effective_read_preference(connection) ).execute_with_connection( connection, - context: Operation::Context.new(client: client, session: session) + context: context ) + else + server.with_connection do |connection| + initial_query_op( + context.session, + effective_read_preference(connection) + ).execute_with_connection( + connection, + context: context + ) + end end end - - # Skip, sort, limit, projection are specified as pipeline stages - # rather than as options. - def cache_options - { - namespace: collection.namespace, - selector: pipeline, - read_concern: view.read_concern, - read_preference: view.read_preference, - collation: options[:collation], - # Aggregations can read documents from more than one collection, - # so they will be cleared on every write operation. - multi_collection: true, - } - end end end end diff --git a/lib/mongo/collection/view/aggregation/behavior.rb b/lib/mongo/collection/view/aggregation/behavior.rb new file mode 100644 index 0000000000..349b82e4bc --- /dev/null +++ b/lib/mongo/collection/view/aggregation/behavior.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +module Mongo + class Collection + class View + class Aggregation + # Distills the behavior common to aggregator classes, like + # View::Aggregator and View::ChangeStream. + module Behavior + extend Forwardable + include Enumerable + include Immutable + include Iterable + include Explainable + include Loggable + include Retryable + + # @return [ View ] view The collection view. + attr_reader :view + + # Delegate necessary operations to the view. + def_delegators :view, :collection, :read, :cluster, :cursor_type, :limit, :batch_size + + # Delegate necessary operations to the collection. + def_delegators :collection, :database, :client + + # Set to true if disk usage is allowed during the aggregation. + # + # @example Set disk usage flag. + # aggregation.allow_disk_use(true) + # + # @param [ true, false ] value The flag value. + # + # @return [ true, false, Aggregation ] The aggregation if a value was + # set or the value if used as a getter. + # + # @since 2.0.0 + def allow_disk_use(value = nil) + configure(:allow_disk_use, value) + end + + # Get the explain plan for the aggregation. + # + # @example Get the explain plan for the aggregation. + # aggregation.explain + # + # @return [ Hash ] The explain plan. + # + # @since 2.0.0 + def explain + self.class.new(view, pipeline, options.merge(explain: true)).first + end + + # Whether this aggregation will write its result to a database collection. + # + # @return [ Boolean ] Whether the aggregation will write its result + # to a collection. + # + # @api private + def write? + pipeline.any? { |op| op.key?('$out') || op.key?(:$out) || op.key?('$merge') || op.key?(:$merge) } + end + + # @return [ Integer | nil ] the timeout_ms value that was passed as + # an option to this object, or which was inherited from the view. + # + # @api private + def timeout_ms + @timeout_ms || view.timeout_ms + end + + private + + # Common setup for all classes that include this behavior; the + # constructor should invoke this method. + def perform_setup(view, options, forbid: []) + @view = view + + @timeout_ms = options.delete(:timeout_ms) + @options = BSON::Document.new(options).freeze + + yield + + validate_timeout_mode!(options, forbid: forbid) + end + + def server_selector + @view.send(:server_selector) + end + + def aggregate_spec(session, read_preference) + Builder::Aggregation.new( + pipeline, + view, + options.merge(session: session, read_preference: read_preference) + ).specification + end + + # Skip, sort, limit, projection are specified as pipeline stages + # rather than as options. + def cache_options + { + namespace: collection.namespace, + selector: pipeline, + read_concern: view.read_concern, + read_preference: view.read_preference, + collation: options[:collation], + # Aggregations can read documents from more than one collection, + # so they will be cleared on every write operation. + multi_collection: true, + } + end + + # @return [ Hash ] timeout_ms value set on the operation level (if any), + # and/or timeout_ms that is set on collection/database/client level (if any). + # + # @api private + def operation_timeouts(opts = {}) + {}.tap do |result| + if opts[:timeout_ms] || @timeout_ms + result[:operation_timeout_ms] = opts.delete(:timeout_ms) || @timeout_ms + else + result[:inherited_timeout_ms] = view.timeout_ms + end + end + end + end + end + end + end +end diff --git a/lib/mongo/collection/view/builder/aggregation.rb b/lib/mongo/collection/view/builder/aggregation.rb index 964e0b3d48..d60000d5a9 100644 --- a/lib/mongo/collection/view/builder/aggregation.rb +++ b/lib/mongo/collection/view/builder/aggregation.rb @@ -113,17 +113,11 @@ def aggregation_command command[:readConcern] = Options::Mapper.transform_values_to_strings( read_concern) end - command[:cursor] = cursor if cursor + command[:cursor] = batch_size_doc command.merge!(Options::Mapper.transform_documents(options, MAPPINGS)) command end - def cursor - if options[:use_cursor] == true || options[:use_cursor].nil? - batch_size_doc - end - end - def batch_size_doc value = options[:batch_size] || view.batch_size if value == 0 && write? diff --git a/lib/mongo/collection/view/change_stream.rb b/lib/mongo/collection/view/change_stream.rb index ee2d9c23bd..3bcce42989 100644 --- a/lib/mongo/collection/view/change_stream.rb +++ b/lib/mongo/collection/view/change_stream.rb @@ -15,6 +15,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'mongo/collection/view/aggregation/behavior' require 'mongo/collection/view/change_stream/retryable' module Mongo @@ -35,7 +36,8 @@ class View # # # @since 2.5.0 - class ChangeStream < Aggregation + class ChangeStream + include Aggregation::Behavior include Retryable # @return [ String ] The fullDocument option default value. @@ -60,6 +62,10 @@ class ChangeStream < Aggregation # @since 2.5.0 attr_reader :options + # @return [ Cursor ] the underlying cursor for this operation + # @api private + attr_reader :cursor + # Initialize the change stream for the provided collection view, pipeline # and options. # @@ -125,11 +131,13 @@ class ChangeStream < Aggregation # # @since 2.5.0 def initialize(view, pipeline, changes_for, options = {}) - @view = view - @changes_for = changes_for - @change_stream_filters = pipeline && pipeline.dup - @options = options && options.dup.freeze - @start_after = @options[:start_after] + # change stream cursors can only be :iterable, so we don't allow + # timeout_mode to be specified. + perform_setup(view, options, forbid: %i[ timeout_mode ]) do + @changes_for = changes_for + @change_stream_filters = pipeline && pipeline.dup + @start_after = @options[:start_after] + end # The resume token tracked by the change stream, used only # when there is no cursor, or no cursor resume token @@ -181,24 +189,30 @@ def each # @return [ BSON::Document | nil ] A change stream document. # @since 2.6.0 def try_next + recreate_cursor! if @timed_out + raise StopIteration.new if closed? + begin doc = @cursor.try_next rescue Mongo::Error => e - if !e.change_stream_resumable? - raise - end - - # Rerun initial aggregation. - # Any errors here will stop iteration and break out of this - # method. + # "If a next call fails with a timeout error, drivers MUST NOT + # invalidate the change stream. The subsequent next call MUST + # perform a resume attempt to establish a new change stream on the + # server..." + # + # However, SocketTimeoutErrors are TimeoutErrors, but are also + # change-stream-resumable. To preserve existing (specified) behavior, + # We only count timeouts when the error is not also + # change-stream-resumable. + @timed_out = e.is_a?(Mongo::Error::TimeoutError) && !e.change_stream_resumable? + + raise unless @timed_out || e.change_stream_resumable? - # Save cursor's resume token so we can use it - # to create a new cursor @resume_token = @cursor.resume_token + raise e if @timed_out - close - create_cursor! + recreate_cursor!(@cursor.context) retry end @@ -231,14 +245,17 @@ def try_next # This method ignores any errors that occur when closing the # server-side cursor. # + # @params [ Hash ] opts Options to be passed to the cursor close + # command. + # # @return [ nil ] Always nil. # # @since 2.5.0 - def close + def close(opts = {}) unless closed? begin - @cursor.close - rescue Error::OperationFailure, Error::SocketError, Error::SocketTimeoutError, Error::MissingConnection + @cursor.close(opts) + rescue Error::OperationFailure::Family, Error::SocketError, Error::SocketTimeoutError, Error::MissingConnection # ignore end @cursor = nil @@ -284,6 +301,28 @@ def resume_token cursor_resume_token || @resume_token end + # "change streams are an abstraction around tailable-awaitData cursors..." + # + # @return :tailable_await + def cursor_type + :tailable_await + end + + # "change streams...implicitly use ITERATION mode" + # + # @return :iteration + def timeout_mode + :iteration + end + + # Returns the value of the max_await_time_ms option that was + # passed to this change stream. + # + # @return [ Integer | nil ] the max_await_time_ms value + def max_await_time_ms + options[:max_await_time_ms] + end + private def for_cluster? @@ -298,19 +337,23 @@ def for_collection? !for_cluster? && !for_database? end - def create_cursor! + def create_cursor!(timeout_ms = nil) # clear the cache because we may get a newer or an older server # (rolling upgrades) @start_at_operation_time_supported = nil - session = client.send(:get_session, @options) + session = client.get_session(@options) + context = Operation::Context.new(client: client, session: session, view: self, operation_timeouts: timeout_ms ? { operation_timeout_ms: timeout_ms } : operation_timeouts) + start_at_operation_time = nil start_at_operation_time_supported = nil - @cursor = read_with_retry_cursor(session, server_selector, view) do |server| + + @cursor = read_with_retry_cursor(session, server_selector, self, context: context) do |server| server.with_connection do |connection| start_at_operation_time_supported = connection.description.server_version_gte?('4.0') - result = send_initial_query(connection, session) + result = send_initial_query(connection, context) + if doc = result.replies.first && result.replies.first.documents.first start_at_operation_time = doc['operationTime'] else @@ -324,6 +367,7 @@ def create_cursor! result end end + @start_at_operation_time = start_at_operation_time @start_at_operation_time_supported = start_at_operation_time_supported end @@ -390,11 +434,11 @@ def change_doc end end - def send_initial_query(connection, session) - initial_query_op(session, view.read_preference) + def send_initial_query(connection, context) + initial_query_op(context.session, view.read_preference) .execute_with_connection( connection, - context: Operation::Context.new(client: client, session: session), + context: context, ) end @@ -412,6 +456,15 @@ def time_to_bson_timestamp(time) def resuming? !!@resuming end + + # Recreates the current cursor (typically as a consequence of attempting + # to resume the change stream) + def recreate_cursor!(context = nil) + @timed_out = false + + close + create_cursor!(context&.remaining_timeout_ms) + end end end end diff --git a/lib/mongo/collection/view/iterable.rb b/lib/mongo/collection/view/iterable.rb index 83ec0e458b..99133c5e9f 100644 --- a/lib/mongo/collection/view/iterable.rb +++ b/lib/mongo/collection/view/iterable.rb @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'mongo/cursor_host' + module Mongo class Collection class View @@ -24,13 +26,7 @@ class View # # @since 2.0.0 module Iterable - - # Returns the cursor associated with this view, if any. - # - # @return [ nil | Cursor ] The cursor, if any. - # - # @api private - attr_reader :cursor + include Mongo::CursorHost # Iterate through documents returned by a query with this +View+. # @@ -45,48 +41,21 @@ module Iterable # # @yieldparam [ Hash ] Each matching document. def each - # If the caching cursor is closed and was not fully iterated, - # the documents we have in it are not the complete result set and - # we have no way of completing that iteration. - # Therefore, discard that cursor and start iteration again. - # The case of the caching cursor not being closed and not having - # been fully iterated isn't tested - see RUBY-2773. - @cursor = if use_query_cache? && cached_cursor && ( - cached_cursor.fully_iterated? || !cached_cursor.closed? - ) - cached_cursor - else - session = client.send(:get_session, @options) - select_cursor(session).tap do |cursor| - if use_query_cache? - # No need to store the cursor in the query cache if there is - # already a cached cursor stored at this key. - QueryCache.set(cursor, **cache_options) - end - end - end + @cursor = prefer_cached_cursor? ? cached_cursor : new_cursor_for_iteration + return @cursor.to_enum unless block_given? - if use_query_cache? - # If a query with a limit is performed, the query cache will - # re-use results from an earlier query with the same or larger - # limit, and then impose the lower limit during iteration. - limit_for_cached_query = respond_to?(:limit) ? QueryCache.normalized_limit(limit) : nil - end + limit_for_cached_query = compute_limit_for_cached_query - if block_given? - # Ruby versions 2.5 and older do not support arr[0..nil] syntax, so - # this must be a separate conditional. - cursor_to_iterate = if limit_for_cached_query - @cursor.to_a[0...limit_for_cached_query] - else - @cursor - end - - cursor_to_iterate.each do |doc| - yield doc - end + # Ruby versions 2.5 and older do not support arr[0..nil] syntax, so + # this must be a separate conditional. + cursor_to_iterate = if limit_for_cached_query + @cursor.to_a[0...limit_for_cached_query] else - @cursor.to_enum + @cursor + end + + cursor_to_iterate.each do |doc| + yield doc end end @@ -100,7 +69,7 @@ def each # # @return [ nil ] Always nil. # - # @raise [ Error::OperationFailure ] If the server cursor close fails. + # @raise [ Error::OperationFailure::Family ] If the server cursor close fails. # # @since 2.1.0 def close_query @@ -113,18 +82,25 @@ def close_query private def select_cursor(session) + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts, + view: self + ) + if respond_to?(:write?, true) && write? server = server_selector.select_server(cluster, nil, session, write_aggregation: true) - result = send_initial_query(server, session) + result = send_initial_query(server, context) if use_query_cache? - CachingCursor.new(view, result, server, session: session) + CachingCursor.new(view, result, server, session: session, context: context) else - Cursor.new(view, result, server, session: session) + Cursor.new(view, result, server, session: session, context: context) end else - read_with_retry_cursor(session, server_selector, view) do |server| - send_initial_query(server, session) + read_with_retry_cursor(session, server_selector, view, context: context) do |server| + send_initial_query(server, context) end end end @@ -168,18 +144,13 @@ def initial_query_op(session) batch_size: batch_size, hint: options[:hint], max_scan: options[:max_scan], - max_time_ms: options[:max_time_ms], max_value: options[:max_value], min_value: options[:min_value], no_cursor_timeout: options[:no_cursor_timeout], return_key: options[:return_key], show_disk_loc: options[:show_disk_loc], comment: options[:comment], - oplog_replay: if (v = options[:oplog_replay]).nil? - collection.options[:oplog_replay] - else - v - end, + oplog_replay: oplog_replay } if spec[:oplog_replay] @@ -196,14 +167,52 @@ def initial_query_op(session) end end - def send_initial_query(server, session = nil) - initial_query_op(session).execute(server, context: Operation::Context.new(client: client, session: session)) + def send_initial_query(server, context) + operation = initial_query_op(context.session) + if server.load_balancer? + # Connection will be checked in when cursor is drained. + connection = server.pool.check_out(context: context) + operation.execute_with_connection(connection, context: context) + else + operation.execute(server, context: context) + end end def use_query_cache? QueryCache.enabled? && !collection.system_collection? end + # If the caching cursor is closed and was not fully iterated, + # the documents we have in it are not the complete result set and + # we have no way of completing that iteration. + # Therefore, discard that cursor and start iteration again. + def prefer_cached_cursor? + use_query_cache? && + cached_cursor && + (cached_cursor.fully_iterated? || !cached_cursor.closed?) + end + + # Start a new cursor for use when iterating (via #each). + def new_cursor_for_iteration + session = client.get_session(@options) + select_cursor(session).tap do |cursor| + if use_query_cache? + # No need to store the cursor in the query cache if there is + # already a cached cursor stored at this key. + QueryCache.set(cursor, **cache_options) + end + end + end + + def compute_limit_for_cached_query + return nil unless use_query_cache? && respond_to?(:limit) + + # If a query with a limit is performed, the query cache will + # re-use results from an earlier query with the same or larger + # limit, and then impose the lower limit during iteration. + return QueryCache.normalized_limit(limit) + end + # Add tailable cusror options to the command specifiction if needed. # # @param [ Hash ] spec The command specification. @@ -216,6 +225,13 @@ def maybe_set_tailable_options(spec) spec[:await_data] = true end end + + # @return [ true | false | nil ] options[:oplog_replay], if + # set, otherwise the same option from the collection. + def oplog_replay + v = options[:oplog_replay] + v.nil? ? collection.options[:oplog_replay] : v + end end end end diff --git a/lib/mongo/collection/view/map_reduce.rb b/lib/mongo/collection/view/map_reduce.rb index 279ce24409..43ef179421 100644 --- a/lib/mongo/collection/view/map_reduce.rb +++ b/lib/mongo/collection/view/map_reduce.rb @@ -51,7 +51,7 @@ class MapReduce attr_reader :reduce_function # Delegate necessary operations to the view. - def_delegators :view, :collection, :read, :cluster + def_delegators :view, :collection, :read, :cluster, :timeout_ms # Delegate necessary operations to the collection. def_delegators :collection, :database, :client @@ -70,10 +70,18 @@ class MapReduce # @yieldparam [ Hash ] Each matching document. def each @cursor = nil - session = client.send(:get_session, @options) + session = client.get_session(@options) server = cluster.next_primary(nil, session) - result = send_initial_query(server, session, context: Operation::Context.new(client: client, session: session)) - result = send_fetch_query(server, session) unless inline? + context = Operation::Context.new(client: client, session: session, operation_timeouts: view.operation_timeouts) + if server.load_balancer? + # Connection will be checked in when cursor is drained. + connection = server.pool.check_out(context: context) + result = send_initial_query_with_connection(connection, context.session, context: context) + result = send_fetch_query_with_connection(connection, session) unless inline? + else + result = send_initial_query(server, context) + result = send_fetch_query(server, session) unless inline? + end @cursor = Cursor.new(view, result, server, session: session) if block_given? @cursor.each do |doc| @@ -279,9 +287,9 @@ def secondary_ok? out.respond_to?(:keys) && out.keys.first.to_s.downcase == INLINE end - def send_initial_query(server, session, context:) + def send_initial_query(server, context) server.with_connection do |connection| - send_initial_query_with_connection(connection, session, context: context) + send_initial_query_with_connection(connection, context.session, context: context) end end @@ -305,7 +313,7 @@ def find_command_spec(session) Builder::MapReduce.new(map_function, reduce_function, view, options.merge(session: session)).command_specification end - def fetch_query_op(server, session) + def fetch_query_op(session) spec = { coll_name: out_collection_name, db_name: out_database_name, @@ -319,7 +327,16 @@ def fetch_query_op(server, session) end def send_fetch_query(server, session) - fetch_query_op(server, session).execute(server, context: Operation::Context.new(client: client, session: session)) + fetch_query_op(session).execute(server, context: Operation::Context.new(client: client, session: session)) + end + + def send_fetch_query_with_connection(connection, session) + fetch_query_op( + session + ).execute_with_connection( + connection, + context: Operation::Context.new(client: client, session: session) + ) end end end diff --git a/lib/mongo/collection/view/readable.rb b/lib/mongo/collection/view/readable.rb index 412137d40f..05fcc78df7 100644 --- a/lib/mongo/collection/view/readable.rb +++ b/lib/mongo/collection/view/readable.rb @@ -47,12 +47,13 @@ module Readable # @option options [ Hash ] :let Mapping of variables to use in the pipeline. # See the server documentation for details. # @option options [ Integer ] :max_time_ms The maximum amount of time in - # milliseconds to allow the aggregation to run. - # @option options [ true, false ] :use_cursor Indicates whether the command - # will request that the server provide results using a cursor. Note that - # as of server version 3.6, aggregations always provide results using a - # cursor and this option is therefore not valid. + # milliseconds to allow the aggregation to run. This option is deprecated, use + # :timeout_ms instead. # @option options [ Session ] :session The session to use. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @return [ Aggregation ] The aggregation object. # @@ -157,6 +158,10 @@ def comment(comment = nil) # @option opts [ Mongo::Session ] :session The session to use for the operation. # @option opts [ Object ] :comment A user-provided # comment to attach to this command. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @return [ Integer ] The document count. # @@ -182,7 +187,12 @@ def count(opts = {}) read_pref = opts[:read] || read_preference selector = ServerSelector.get(read_pref || server_selector) with_session(opts) do |session| - read_with_retry(session, selector) do |server| + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(opts) + ) + read_with_retry(session, selector, context) do |server| Operation::Count.new( selector: cmd, db_name: database.name, @@ -193,7 +203,10 @@ def count(opts = {}) # string key. Note that this isn't documented as valid usage. collation: opts[:collation] || opts['collation'] || collation, comment: opts[:comment], - ).execute(server, context: Operation::Context.new(client: client, session: session)) + ).execute( + server, + context: context + ) end.n.to_i end end @@ -210,12 +223,17 @@ def count(opts = {}) # MongoDB to use a specific index for the query. Requires server version 3.6+. # @option opts :limit [ Integer ] Max number of docs to count. # @option opts :max_time_ms [ Integer ] The maximum amount of time to allow the - # command to run. + # command to run. This option is deprecated, use + # :timeout_ms instead. # @option opts [ Hash ] :read The read preference options. # @option opts [ Hash ] :collation The collation to use. # @option opts [ Mongo::Session ] :session The session to use for the operation. # @option ops [ Object ] :comment A user-provided # comment to attach to this command. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @return [ Integer ] The document count. # @@ -227,7 +245,7 @@ def count_documents(opts = {}) pipeline << { :'$limit' => opts[:limit] } if opts[:limit] pipeline << { :'$group' => { _id: 1, n: { :'$sum' => 1 } } } - opts = opts.slice(:hint, :max_time_ms, :read, :collation, :session, :comment) + opts = opts.slice(:hint, :max_time_ms, :read, :collation, :session, :comment, :timeout_ms) opts[:collation] ||= collation first = aggregate(pipeline, opts).first @@ -247,6 +265,10 @@ def count_documents(opts = {}) # @option opts [ Hash ] :read The read preference options. # @option opts [ Object ] :comment A user-provided # comment to attach to this command. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @return [ Integer ] The document count. # @@ -267,8 +289,12 @@ def estimated_document_count(opts = {}) read_pref = opts[:read] || read_preference selector = ServerSelector.get(read_pref || server_selector) with_session(opts) do |session| - read_with_retry(session, selector) do |server| - context = Operation::Context.new(client: client, session: session) + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(opts) + ) + read_with_retry(session, selector, context) do |server| cmd = { count: collection.name } cmd[:maxTimeMS] = opts[:max_time_ms] if opts[:max_time_ms] if read_concern @@ -284,7 +310,7 @@ def estimated_document_count(opts = {}) result.n.to_i end end - rescue Error::OperationFailure => exc + rescue Error::OperationFailure::Family => exc if exc.code == 26 # NamespaceNotFound # This should only happen with the aggregation pipeline path @@ -331,7 +357,12 @@ def distinct(field_name, opts = {}) read_pref = opts[:read] || read_preference selector = ServerSelector.get(read_pref || server_selector) with_session(opts) do |session| - read_with_retry(session, selector) do |server| + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(opts) + ) + read_with_retry(session, selector, context) do |server| Operation::Distinct.new( selector: cmd, db_name: database.name, @@ -342,7 +373,10 @@ def distinct(field_name, opts = {}) # For some reason collation was historically accepted as a # string key. Note that this isn't documented as valid usage. collation: opts[:collation] || opts['collation'] || collation, - ).execute(server, context: Operation::Context.new(client: client, session: session)) + ).execute( + server, + context: context + ) end.first['values'] end end @@ -627,6 +661,15 @@ def cursor_type(type = nil) configure(:cursor_type, type) end + # The per-operation timeout in milliseconds. Must a positive integer. + # + # @param [ Integer ] timeout_ms Timeout value. + # + # @return [ Integer, View ] Either the timeout_ms value or a new +View+. + def timeout_ms(timeout_ms = nil) + configure(:timeout_ms, timeout_ms) + end + # @api private def read_concern if options[:session] && options[:session].in_transaction? @@ -656,24 +699,10 @@ def read_preference end end - private - - def collation(doc = nil) - configure(:collation, doc) - end - - def server_selector - @server_selector ||= if options[:session] && options[:session].in_transaction? - ServerSelector.get(read_preference || client.server_selector) - else - ServerSelector.get(read_preference || collection.server_selector) - end - end - def parallel_scan(cursor_count, options = {}) if options[:session] # The session would be overwritten by the one in +options+ later. - session = client.send(:get_session, @options) + session = client.get_session(@options) else session = nil end @@ -707,11 +736,31 @@ def parallel_scan(cursor_count, options = {}) session: session, connection_global_id: result.connection_global_id, ) - result = op.execute(server, context: context) + result = if server.load_balancer? + # Connection will be checked in when cursor is drained. + connection = server.pool.check_out(context: context) + op.execute_with_connection(connection, context: context) + else + op.execute(server, context: context) + end Cursor.new(self, result, server, session: session) end end + private + + def collation(doc = nil) + configure(:collation, doc) + end + + def server_selector + @server_selector ||= if options[:session] && options[:session].in_transaction? + ServerSelector.get(read_preference || client.server_selector) + else + ServerSelector.get(read_preference || collection.server_selector) + end + end + def validate_doc!(doc) raise Error::InvalidDocument.new unless doc.respond_to?(:keys) end diff --git a/lib/mongo/collection/view/writable.rb b/lib/mongo/collection/view/writable.rb index 40bc230725..0a1f553b1d 100644 --- a/lib/mongo/collection/view/writable.rb +++ b/lib/mongo/collection/view/writable.rb @@ -38,7 +38,8 @@ module Writable # @param [ Hash ] opts The options. # # @option opts [ Integer ] :max_time_ms The maximum amount of time to allow the command - # to run in milliseconds. + # to run in milliseconds. This option is deprecated, use + # :timeout_ms instead. # @option opts [ Hash ] :projection The fields to include or exclude in the returned doc. # @option opts [ Hash ] :sort The key and direction pairs by which the result set # will be sorted. @@ -46,11 +47,15 @@ module Writable # @option opts [ Session ] :session The session to use. # @option opts [ Hash | String ] :hint The index to use for this operation. # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option opts [ Hash ] :write_concern The write concern options. # Can be :w => Integer, :fsync => Boolean, :j => Boolean. - # @option options [ Hash ] :let Mapping of variables to use in the command. + # @option opts [ Hash ] :let Mapping of variables to use in the command. # See the server documentation for details. - # @option options [ Object ] :comment A user-provided + # @option opts [ Object ] :comment A user-provided # comment to attach to this command. # # @return [ BSON::Document, nil ] The document, if found. @@ -80,7 +85,11 @@ def find_one_and_delete(opts = {}) comment: opts[:comment], }.compact - context = Operation::Context.new(client: client, session: session) + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(opts) + ) write_with_retry(write_concern, context: context) do |connection, txn_num, context| gte_4_4 = connection.server.description.server_version_gte?('4.4') if !gte_4_4 && opts[:hint] && write_concern && !write_concern.acknowledged? @@ -116,9 +125,13 @@ def find_one_and_delete(opts = {}) # @option opts [ Hash ] :collation The collation to use. # @option opts [ Hash | String ] :hint The index to use for this operation. # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option opts [ Hash ] :write_concern The write concern options. # Can be :w => Integer, :fsync => Boolean, :j => Boolean. - # @option options [ Hash ] :let Mapping of variables to use in the command. + # @option opts [ Hash ] :let Mapping of variables to use in the command. # See the server documentation for details. # # @return [ BSON::Document ] The document. @@ -136,8 +149,9 @@ def find_one_and_replace(replacement, opts = {}) # @param [ BSON::Document ] document The updates. # @param [ Hash ] opts The options. # - # @option options [ Integer ] :max_time_ms The maximum amount of time to allow the command - # to run in milliseconds. + # @option opts [ Integer ] :max_time_ms The maximum amount of time to allow the command + # to run in milliseconds. This option is deprecated, use + # :timeout_ms instead. # @option opts [ Hash ] :projection The fields to include or exclude in the returned doc. # @option opts [ Hash ] :sort The key and direction pairs by which the result set # will be sorted. @@ -149,13 +163,17 @@ def find_one_and_replace(replacement, opts = {}) # @option opts [ Array ] :array_filters A set of filters specifying to which array elements # an update should apply. # @option opts [ Session ] :session The session to use. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option opts [ Hash | String ] :hint The index to use for this operation. # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). # @option opts [ Hash ] :write_concern The write concern options. # Can be :w => Integer, :fsync => Boolean, :j => Boolean. - # @option options [ Hash ] :let Mapping of variables to use in the command. + # @option opts [ Hash ] :let Mapping of variables to use in the command. # See the server documentation for details. - # @option options [ Object ] :comment A user-provided + # @option opts [ Object ] :comment A user-provided # comment to attach to this command. # # @return [ BSON::Document | nil ] The document or nil if none is found. @@ -188,7 +206,11 @@ def find_one_and_update(document, opts = {}) comment: opts[:comment] }.compact - context = Operation::Context.new(client: client, session: session) + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(opts) + ) write_with_retry(write_concern, context: context) do |connection, txn_num, context| gte_4_4 = connection.server.description.server_version_gte?('4.4') if !gte_4_4 && opts[:hint] && write_concern && !write_concern.acknowledged? @@ -216,13 +238,17 @@ def find_one_and_update(document, opts = {}) # # @option opts [ Hash ] :collation The collation to use. # @option opts [ Session ] :session The session to use. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option opts [ Hash | String ] :hint The index to use for this operation. # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). # @option opts [ Hash ] :write_concern The write concern options. # Can be :w => Integer, :fsync => Boolean, :j => Boolean. - # @option options [ Hash ] :let Mapping of variables to use in the command. + # @option opts [ Hash ] :let Mapping of variables to use in the command. # See the server documentation for details. - # @option options [ Object ] :comment A user-provided + # @option opts [ Object ] :comment A user-provided # comment to attach to this command. # # @return [ Result ] The response from the database. @@ -244,8 +270,11 @@ def delete_many(opts = {}) hint: opts[:hint], collation: opts[:collation] || opts['collation'] || collation, }.compact - - context = Operation::Context.new(client: client, session: session) + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(opts) + ) nro_write_with_retry(write_concern, context: context) do |connection, txn_num, context| gte_4_4 = connection.server.description.server_version_gte?('4.4') if !gte_4_4 && opts[:hint] && write_concern && !write_concern.acknowledged? @@ -274,15 +303,19 @@ def delete_many(opts = {}) # @param [ Hash ] opts The options. # # @option opts [ Hash ] :collation The collation to use. - # @option opts [ Session ] :session The session to use. + # @option opts [ Object ] :comment A user-provided + # comment to attach to this command. # @option opts [ Hash | String ] :hint The index to use for this operation. # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). + # @option opts [ Hash ] :let Mapping of variables to use in the command. + # See the server documentation for details. + # @option opts [ Session ] :session The session to use. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option opts [ Hash ] :write_concern The write concern options. # Can be :w => Integer, :fsync => Boolean, :j => Boolean. - # @option options [ Hash ] :let Mapping of variables to use in the command. - # See the server documentation for details. - # @option options [ Object ] :comment A user-provided - # comment to attach to this command. # # @return [ Result ] The response from the database. # @@ -304,7 +337,11 @@ def delete_one(opts = {}) collation: opts[:collation] || opts['collation'] || collation, }.compact - context = Operation::Context.new(client: client, session: session) + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(opts) + ) write_with_retry(write_concern, context: context) do |connection, txn_num, context| gte_4_4 = connection.server.description.server_version_gte?('4.4') if !gte_4_4 && opts[:hint] && write_concern && !write_concern.acknowledged? @@ -334,20 +371,24 @@ def delete_one(opts = {}) # @param [ Hash ] replacement The replacement document. # @param [ Hash ] opts The options. # - # @option opts [ true, false ] :upsert Whether to upsert if the - # document doesn't exist. # @option opts [ true, false ] :bypass_document_validation Whether or # not to skip document level validation. # @option opts [ Hash ] :collation The collation to use. - # @option opts [ Session ] :session The session to use. + # @option opts [ Object ] :comment A user-provided + # comment to attach to this command. # @option opts [ Hash | String ] :hint The index to use for this operation. # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). + # @option opts [ Hash ] :let Mapping of variables to use in the command. + # See the server documentation for details. + # @option opts [ Session ] :session The session to use. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # @option opts [ Hash ] :write_concern The write concern options. + # @option opts [ true, false ] :upsert Whether to upsert if the + # document doesn't exist. # Can be :w => Integer, :fsync => Boolean, :j => Boolean. - # @option options [ Hash ] :let Mapping of variables to use in the command. - # See the server documentation for details. - # @option options [ Object ] :comment A user-provided - # comment to attach to this command. # # @return [ Result ] The response from the database. # @@ -374,7 +415,11 @@ def replace_one(replacement, opts = {}) update_doc['upsert'] = true end - context = Operation::Context.new(client: client, session: session) + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(opts) + ) write_with_retry(write_concern, context: context) do |connection, txn_num, context| gte_4_2 = connection.server.description.server_version_gte?('4.2') if !gte_4_2 && opts[:hint] && write_concern && !write_concern.acknowledged? @@ -404,22 +449,26 @@ def replace_one(replacement, opts = {}) # @param [ Hash | Array ] spec The update document or pipeline. # @param [ Hash ] opts The options. # - # @option opts [ true, false ] :upsert Whether to upsert if the - # document doesn't exist. + # @option opts [ Array ] :array_filters A set of filters specifying to + # which array elements an update should apply. # @option opts [ true, false ] :bypass_document_validation Whether or # not to skip document level validation. # @option opts [ Hash ] :collation The collation to use. - # @option opts [ Array ] :array_filters A set of filters specifying to - # which array elements an update should apply. - # @option opts [ Session ] :session The session to use. + # @option opts [ Object ] :comment A user-provided + # comment to attach to this command. # @option opts [ Hash | String ] :hint The index to use for this operation. # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). + # @option opts [ Hash ] :let Mapping of variables to use in the command. + # See the server documentation for details. + # @option opts [ Session ] :session The session to use. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. + # @option opts [ true, false ] :upsert Whether to upsert if the + # document doesn't exist. # @option opts [ Hash ] :write_concern The write concern options. # Can be :w => Integer, :fsync => Boolean, :j => Boolean. - # @option options [ Hash ] :let Mapping of variables to use in the command. - # See the server documentation for details. - # @option options [ Object ] :comment A user-provided - # comment to attach to this command. # # @return [ Result ] The response from the database. # @@ -447,7 +496,11 @@ def update_many(spec, opts = {}) update_doc['upsert'] = true end - context = Operation::Context.new(client: client, session: session) + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(opts) + ) nro_write_with_retry(write_concern, context: context) do |connection, txn_num, context| gte_4_2 = connection.server.description.server_version_gte?('4.2') if !gte_4_2 && opts[:hint] && write_concern && !write_concern.acknowledged? @@ -476,22 +529,26 @@ def update_many(spec, opts = {}) # @param [ Hash | Array ] spec The update document or pipeline. # @param [ Hash ] opts The options. # - # @option opts [ true, false ] :upsert Whether to upsert if the - # document doesn't exist. + # @option opts [ Array ] :array_filters A set of filters specifying to + # which array elements an update should apply. # @option opts [ true, false ] :bypass_document_validation Whether or # not to skip document level validation. # @option opts [ Hash ] :collation The collation to use. - # @option opts [ Array ] :array_filters A set of filters specifying to - # which array elements an update should apply. - # @option opts [ Session ] :session The session to use. + # @option opts [ Object ] :comment A user-provided + # comment to attach to this command. # @option opts [ Hash | String ] :hint The index to use for this operation. # May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_"). + # @option opts [ Hash ] :let Mapping of variables to use in the command. + # See the server documentation for details. + # @option opts [ Session ] :session The session to use. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. + # @option opts [ true, false ] :upsert Whether to upsert if the + # document doesn't exist. # @option opts [ Hash ] :write_concern The write concern options. # Can be :w => Integer, :fsync => Boolean, :j => Boolean. - # @option options [ Hash ] :let Mapping of variables to use in the command. - # See the server documentation for details. - # @option options [ Object ] :comment A user-provided - # comment to attach to this command. # # @return [ Result ] The response from the database. # @@ -518,7 +575,11 @@ def update_one(spec, opts = {}) update_doc['upsert'] = true end - context = Operation::Context.new(client: client, session: session) + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(opts) + ) write_with_retry(write_concern, context: context) do |connection, txn_num, context| gte_4_2 = connection.server.description.server_version_gte?('4.2') if !gte_4_2 && opts[:hint] && write_concern && !write_concern.acknowledged? diff --git a/lib/mongo/crypt/auto_encrypter.rb b/lib/mongo/crypt/auto_encrypter.rb index a98f7d93a8..3fa8970783 100644 --- a/lib/mongo/crypt/auto_encrypter.rb +++ b/lib/mongo/crypt/auto_encrypter.rb @@ -119,8 +119,6 @@ def initialize(options) @options[:extra_options][:crypt_shared_lib_required] unless @options[:extra_options][:crypt_shared_lib_required] || @crypt_handle.crypt_shared_lib_available? || @options[:bypass_query_analysis] - # Set server selection timeout to 1 to prevent the client waiting for a - # long timeout before spawning mongocryptd @mongocryptd_client = Client.new( @options[:extra_options][:mongocryptd_uri], monitoring_io: @options[:client].options[:monitoring_io], @@ -189,13 +187,13 @@ def encrypt? # @param [ Hash ] command The command to be encrypted. # # @return [ BSON::Document ] The encrypted command. - def encrypt(database_name, command) + def encrypt(database_name, command, timeout_holder) AutoEncryptionContext.new( @crypt_handle, @encryption_io, database_name, command - ).run_state_machine + ).run_state_machine(timeout_holder) end # Decrypt a database command. @@ -203,12 +201,12 @@ def encrypt(database_name, command) # @param [ Hash ] command The command with encrypted fields. # # @return [ BSON::Document ] The decrypted command. - def decrypt(command) + def decrypt(command, timeout_holder) AutoDecryptionContext.new( @crypt_handle, @encryption_io, command - ).run_state_machine + ).run_state_machine(timeout_holder) end # Close the resources created by the AutoEncrypter. diff --git a/lib/mongo/crypt/binding.rb b/lib/mongo/crypt/binding.rb index 906f76facc..98eabb9ce8 100644 --- a/lib/mongo/crypt/binding.rb +++ b/lib/mongo/crypt/binding.rb @@ -1699,9 +1699,9 @@ def self.ctx_setopt_contention_factor(context, factor) # @!method self.mongocrypt_ctx_setopt_algorithm_range(ctx, opts) # @api private # - # Set options for explicit encryption with the "rangePreview" algorithm. + # Set options for explicit encryption with the "range" algorithm. # - # @note The RangePreview algorithm is experimental only. It is not intended for + # @note The Range algorithm is experimental only. It is not intended for # public use. # # @param [ FFI::Pointer ] ctx A pointer to a mongocrypt_ctx_t object. @@ -1718,9 +1718,9 @@ def self.ctx_setopt_contention_factor(context, factor) :bool ) - # Set options for explicit encryption with the "rangePreview" algorithm. + # Set options for explicit encryption with the "range" algorithm. # - # @note The RangePreview algorithm is experimental only. It is not intended for + # @note The Range algorithm is experimental only. It is not intended for # public use. # # @param [ Mongo::Crypt::Context ] context diff --git a/lib/mongo/crypt/context.rb b/lib/mongo/crypt/context.rb index 5ace2b9dde..d8c6772999 100644 --- a/lib/mongo/crypt/context.rb +++ b/lib/mongo/crypt/context.rb @@ -64,7 +64,10 @@ def state end # Runs the mongocrypt_ctx_t state machine and handles - # all I/O on behalf of libmongocrypt + # all I/O on behalf of + # + # @param [ CsotTimeoutHolder ] timeout_holder CSOT timeouts for the + # operation the state. # # @return [ BSON::Document ] A BSON document representing the outcome # of the state machine. Contents can differ depending on how the @@ -75,8 +78,9 @@ def state # # This method is not currently unit tested. It is integration tested # in spec/integration/explicit_encryption_spec.rb - def run_state_machine + def run_state_machine(timeout_holder) while true + timeout_ms = timeout_holder.remaining_timeout_ms! case state when :error Binding.check_ctx_status(self) @@ -88,7 +92,7 @@ def run_state_machine when :need_mongo_keys filter = Binding.ctx_mongo_op(self) - @encryption_io.find_keys(filter).each do |key| + @encryption_io.find_keys(filter, timeout_ms: timeout_ms).each do |key| mongocrypt_feed(key) if key end @@ -96,14 +100,14 @@ def run_state_machine when :need_mongo_collinfo filter = Binding.ctx_mongo_op(self) - result = @encryption_io.collection_info(@db_name, filter) + result = @encryption_io.collection_info(@db_name, filter, timeout_ms: timeout_ms) mongocrypt_feed(result) if result mongocrypt_done when :need_mongo_markings cmd = Binding.ctx_mongo_op(self) - result = @encryption_io.mark_command(cmd) + result = @encryption_io.mark_command(cmd, timeout_ms: timeout_ms) mongocrypt_feed(result) mongocrypt_done @@ -118,7 +122,7 @@ def run_state_machine when :need_kms_credentials Binding.ctx_provide_kms_providers( self, - retrieve_kms_credentials.to_document + retrieve_kms_credentials(timeout_holder).to_document ) else raise Error::CryptError.new( @@ -147,13 +151,15 @@ def mongocrypt_feed(doc) # Retrieves KMS credentials for providers that are configured # for automatic credentials retrieval. # + # @param [ CsotTimeoutHolder ] timeout_holder CSOT timeout. + # # @return [ Crypt::KMS::Credentials ] Credentials for the configured # KMS providers. - def retrieve_kms_credentials + def retrieve_kms_credentials(timeout_holder) providers = {} if kms_providers.aws&.empty? begin - aws_credentials = Mongo::Auth::Aws::CredentialsRetriever.new.credentials + aws_credentials = Mongo::Auth::Aws::CredentialsRetriever.new.credentials(timeout_holder) rescue Auth::Aws::CredentialsNotFound raise Error::CryptError.new( "Could not locate AWS credentials (checked environment variables, ECS and EC2 metadata)" @@ -162,10 +168,10 @@ def retrieve_kms_credentials providers[:aws] = aws_credentials.to_h end if kms_providers.gcp&.empty? - providers[:gcp] = { access_token: gcp_access_token } + providers[:gcp] = { access_token: gcp_access_token(timeout_holder) } end if kms_providers.azure&.empty? - providers[:azure] = { access_token: azure_access_token } + providers[:azure] = { access_token: azure_access_token(timeout_holder) } end KMS::Credentials.new(providers) end @@ -175,8 +181,8 @@ def retrieve_kms_credentials # @return [ String ] A GCP access token. # # @raise [ Error::CryptError ] If the GCP access token could not be - def gcp_access_token - KMS::GCP::CredentialsRetriever.fetch_access_token + def gcp_access_token(timeout_holder) + KMS::GCP::CredentialsRetriever.fetch_access_token(timeout_holder) rescue KMS::CredentialsNotFound => e raise Error::CryptError.new( "Could not locate GCP credentials: #{e.class}: #{e.message}" @@ -189,9 +195,9 @@ def gcp_access_token # # @raise [ Error::CryptError ] If the Azure access token could not be # retrieved. - def azure_access_token + def azure_access_token(timeout_holder) if @cached_azure_token.nil? || @cached_azure_token.expired? - @cached_azure_token = KMS::Azure::CredentialsRetriever.fetch_access_token + @cached_azure_token = KMS::Azure::CredentialsRetriever.fetch_access_token(timeout_holder: timeout_holder) end @cached_azure_token.access_token rescue KMS::CredentialsNotFound => e diff --git a/lib/mongo/crypt/encryption_io.rb b/lib/mongo/crypt/encryption_io.rb index 67dee5731f..6bd5bccf4e 100644 --- a/lib/mongo/crypt/encryption_io.rb +++ b/lib/mongo/crypt/encryption_io.rb @@ -73,47 +73,66 @@ def initialize( # filter # # @param [ Hash ] filter + # @param [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the feature is not enabled. # # @return [ Array ] The query results - def find_keys(filter) - key_vault_collection.find(filter).to_a + def find_keys(filter, timeout_ms: nil) + key_vault_collection.find(filter, timeout_ms: timeout_ms).to_a end # Insert a document into the key vault collection # # @param [ Hash ] document + # @param [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the feature is not enabled. # # @return [ Mongo::Operation::Insert::Result ] The insertion result - def insert_data_key(document) - key_vault_collection.insert_one(document) + def insert_data_key(document, timeout_ms: nil) + key_vault_collection.insert_one(document, timeout_ms: timeout_ms) end # Get collection info for a collection matching the provided filter # # @param [ Hash ] filter + # @param [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the feature is not enabled. # # @return [ Hash ] The collection information - def collection_info(db_name, filter) + def collection_info(db_name, filter, timeout_ms: nil) unless @metadata_client raise ArgumentError, 'collection_info requires metadata_client to have been passed to the constructor, but it was not' end - @metadata_client.use(db_name).database.list_collections(filter: filter, deserialize_as_bson: true).first + @metadata_client + .use(db_name) + .database + .list_collections(filter: filter, deserialize_as_bson: true, timeout_ms: timeout_ms) + .first end # Send the command to mongocryptd to be marked with intent-to-encrypt markings # # @param [ Hash ] cmd + # @param [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the feature is not enabled. # # @return [ Hash ] The marked command - def mark_command(cmd) + def mark_command(cmd, timeout_ms: nil) unless @mongocryptd_client raise ArgumentError, 'mark_command requires mongocryptd_client to have been passed to the constructor, but it was not' end # Ensure the response from mongocryptd is deserialized with { mode: :bson } # to prevent losing type information in commands - options = { execution_options: { deserialize_as_bson: true } } + options = { + execution_options: { deserialize_as_bson: true }, + timeout_ms: timeout_ms + } begin response = @mongocryptd_client.database.command(cmd, options) @@ -136,9 +155,12 @@ def mark_command(cmd) # to send on that connection. # @param [ Hash ] tls_options. TLS options to connect to KMS provider. # The options are same as for Mongo::Client. - def feed_kms(kms_context, tls_options) + # @param [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the feature is not enabled. + def feed_kms(kms_context, tls_options, timeout_ms: nil) with_ssl_socket(kms_context.endpoint, tls_options) do |ssl_socket| - Timeout.timeout(SOCKET_TIMEOUT, Error::SocketTimeoutError, + Timeout.timeout(timeout_ms || SOCKET_TIMEOUT, Error::SocketTimeoutError, 'Socket write operation timed out' ) do ssl_socket.syswrite(kms_context.message) @@ -146,7 +168,7 @@ def feed_kms(kms_context, tls_options) bytes_needed = kms_context.bytes_needed while bytes_needed > 0 do - bytes = Timeout.timeout(SOCKET_TIMEOUT, Error::SocketTimeoutError, + bytes = Timeout.timeout(timeout_ms || SOCKET_TIMEOUT, Error::SocketTimeoutError, 'Socket read operation timed out' ) do ssl_socket.sysread(bytes_needed) @@ -160,38 +182,39 @@ def feed_kms(kms_context, tls_options) # Adds a key_alt_name to the key_alt_names array of the key document # in the key vault collection with the given id. - def add_key_alt_name(id, key_alt_name) + def add_key_alt_name(id, key_alt_name, timeout_ms: nil) key_vault_collection.find_one_and_update( { _id: id }, { '$addToSet' => { keyAltNames: key_alt_name } }, + timeout_ms: timeout_ms ) end # Removes the key document with the given id # from the key vault collection. - def delete_key(id) - key_vault_collection.delete_one(_id: id) + def delete_key(id, timeout_ms: nil) + key_vault_collection.delete_one(_id: id, timeout_ms: timeout_ms) end # Finds a single key document with the given id. - def get_key(id) - key_vault_collection.find(_id: id).first + def get_key(id, timeout_ms: nil) + key_vault_collection.find(_id: id, timeout_ms: timeout_ms).first end # Returns a key document in the key vault collection with # the given key_alt_name. - def get_key_by_alt_name(key_alt_name) - key_vault_collection.find(keyAltNames: key_alt_name).first + def get_key_by_alt_name(key_alt_name, timeout_ms: nil) + key_vault_collection.find(keyAltNames: key_alt_name, timeout_ms: timeout_ms).first end # Finds all documents in the key vault collection. - def get_keys - key_vault_collection.find + def get_keys(timeout_ms: nil) + key_vault_collection.find(nil, timeout_ms: timeout_ms) end # Removes a key_alt_name from the key_alt_names array of the key document # in the key vault collection with the given id. - def remove_key_alt_name(id, key_alt_name) + def remove_key_alt_name(id, key_alt_name, timeout_ms: nil) key_vault_collection.find_one_and_update( { _id: id }, [ @@ -211,7 +234,8 @@ def remove_key_alt_name(id, key_alt_name) } } } - ] + ], + timeout_ms: timeout_ms ) end @@ -220,8 +244,8 @@ def remove_key_alt_name(id, key_alt_name) # @param [ Array ] requests The bulk write requests. # # @return [ BulkWrite::Result ] The result of the operation. - def update_data_keys(updates) - key_vault_collection.bulk_write(updates) + def update_data_keys(updates, timeout_ms: nil) + key_vault_collection.bulk_write(updates, timeout_ms: timeout_ms) end private @@ -322,15 +346,21 @@ def spawn_mongocryptd # # @note The socket is always closed when the provided block has finished # executing - def with_ssl_socket(endpoint, tls_options) + def with_ssl_socket(endpoint, tls_options, timeout_ms: nil) + csot = !timeout_ms.nil? address = begin host, port = endpoint.split(':') port ||= 443 # All supported KMS APIs use this port by default. Address.new([host, port].join(':')) end + socket_options = { ssl: true, csot: csot }.tap do |opts| + if csot + opts[:connect_timeout] = (timeout_ms / 1_000.0) + end + end mongo_socket = address.socket( SOCKET_TIMEOUT, - tls_options.merge(ssl: true) + tls_options.merge(socket_options) ) yield(mongo_socket.socket) rescue => e diff --git a/lib/mongo/crypt/explicit_encrypter.rb b/lib/mongo/crypt/explicit_encrypter.rb index 946f97d7bd..9feb8f7dbb 100644 --- a/lib/mongo/crypt/explicit_encrypter.rb +++ b/lib/mongo/crypt/explicit_encrypter.rb @@ -35,7 +35,9 @@ class ExplicitEncrypter # providers. Keys of the hash should be KSM provider names; values # should be hashes of TLS connection options. The options are equivalent # to TLS connection options of Mongo::Client. - def initialize(key_vault_client, key_vault_namespace, kms_providers, kms_tls_options) + # @param [ Integer | nil ] timeout_ms Timeout for every operation executed + # on this object. + def initialize(key_vault_client, key_vault_namespace, kms_providers, kms_tls_options, timeout_ms = nil) Crypt.validate_ffi! @crypt_handle = Handle.new( kms_providers, @@ -47,6 +49,7 @@ def initialize(key_vault_client, key_vault_namespace, kms_providers, kms_tls_opt metadata_client: nil, key_vault_namespace: key_vault_namespace ) + @timeout_ms = timeout_ms end # Generates a data key used for encryption/decryption and stores @@ -71,9 +74,11 @@ def create_and_insert_data_key(master_key_document, key_alt_names, key_material master_key_document, key_alt_names, key_material - ).run_state_machine + ).run_state_machine(timeout_holder) - @encryption_io.insert_data_key(data_key_document).inserted_id + @encryption_io.insert_data_key( + data_key_document, timeout_ms: timeout_holder.remaining_timeout_ms! + ).inserted_id end # Encrypts a value using the specified encryption key and algorithm @@ -111,7 +116,7 @@ def encrypt(value, options) @encryption_io, { v: value }, options - ).run_state_machine['v'] + ).run_state_machine(timeout_holder)['v'] end # Encrypts a Match Expression or Aggregate Expression to query a range index. @@ -125,7 +130,7 @@ def encrypt(value, options) # {'$and' => [{'$gt' => ['$field', 10]}, {'$lt' => ['$field', 20]}} # ) # {$and: [{$gt: [, ]}, {$lt: [, ]}] - # Only supported when queryType is "rangePreview" and algorithm is "RangePreview". + # Only supported when queryType is "range" and algorithm is "Range". # @note: The Range algorithm is experimental only. It is not intended # for public use. It is subject to breaking changes. # @@ -137,24 +142,25 @@ def encrypt(value, options) # @option options [ String ] :key_alt_name The alternate name for the # encryption key. # @option options [ String ] :algorithm The algorithm used to encrypt the - # expression. The only allowed value is "RangePreview" + # expression. The only allowed value is "Range" # @option options [ Integer | nil ] :contention_factor Contention factor # to be applied If not provided, it defaults to a value of 0. # @option options [ String | nil ] query_type Query type to be applied. - # The only allowed value is "rangePreview". + # The only allowed value is "range". # @option options [ Hash | nil ] :range_opts Specifies index options for - # a Queryable Encryption field supporting "rangePreview" queries. + # a Queryable Encryption field supporting "range" queries. # Allowed options are: # - :min # - :max + # - :trim_factor # - :sparsity # - :precision - # min, max, sparsity, and range must match the values set in + # min, max, trim_factor, sparsity, and precision must match the values set in # the encryptedFields of the destination collection. # For double and decimal128, min/max/precision must all be set, # or all be unset. # - # @note The RangePreview algorithm is experimental only. It is not + # @note The Range algorithm is experimental only. It is not # intended for public use. # # @note The :key_id and :key_alt_name options are mutually exclusive. Only @@ -170,7 +176,7 @@ def encrypt_expression(expression, options) @encryption_io, { v: expression }, options - ).run_state_machine['v'] + ).run_state_machine(timeout_holder)['v'] end # Decrypts a value that has already been encrypted @@ -184,7 +190,7 @@ def decrypt(value) @crypt_handle, @encryption_io, { v: value } - ).run_state_machine['v'] + ).run_state_machine(timeout_holder)['v'] end # Adds a key_alt_name for the key in the key vault collection with the given id. @@ -195,7 +201,7 @@ def decrypt(value) # @return [ BSON::Document | nil ] Document describing the identified key # before adding the key alt name, or nil if no such key. def add_key_alt_name(id, key_alt_name) - @encryption_io.add_key_alt_name(id, key_alt_name) + @encryption_io.add_key_alt_name(id, key_alt_name, timeout_ms: @timeout_ms) end # Removes the key with the given id from the key vault collection. @@ -204,7 +210,9 @@ def add_key_alt_name(id, key_alt_name) # # @return [ Operation::Result ] The response from the database for the delete_one # operation that deletes the key. - def_delegators :@encryption_io, :delete_key + def delete_key(id) + @encryption_io.delete_key(id, timeout_ms: @timeout_ms) + end # Finds a single key with the given id. # @@ -212,7 +220,9 @@ def add_key_alt_name(id, key_alt_name) # # @return [ BSON::Document | nil ] The found key document or nil # if not found. - def_delegators :@encryption_io, :get_key + def get_key(id) + @encryption_io.get_key(id, timeout_ms: @timeout_ms) + end # Returns a key in the key vault collection with the given key_alt_name. # @@ -220,12 +230,19 @@ def add_key_alt_name(id, key_alt_name) # # @return [ BSON::Document | nil ] The found key document or nil # if not found. - def_delegators :@encryption_io, :get_key_by_alt_name + def get_key_by_alt_name(key_alt_name) + @encryption_io.get_key_by_alt_name(key_alt_name, timeout_ms: @timeout_ms) + end # Returns all keys in the key vault collection. # # @return [ Collection::View ] Keys in the key vault collection. - def_delegators :@encryption_io, :get_keys + # rubocop:disable Naming/AccessorMethodName + # Name of this method is defined in the FLE spec + def get_keys + @encryption_io.get_keys(timeout_ms: @timeout_ms) + end + # rubocop:enable Naming/AccessorMethodName # Removes a key_alt_name from a key in the key vault collection with the given id. # @@ -234,7 +251,9 @@ def add_key_alt_name(id, key_alt_name) # # @return [ BSON::Document | nil ] Document describing the identified key # before removing the key alt name, or nil if no such key. - def_delegators :@encryption_io, :remove_key_alt_name + def remove_key_alt_name(id, key_alt_name) + @encryption_io.remove_key_alt_name(id, key_alt_name, timeout_ms: @timeout_ms) + end # Decrypts multiple data keys and (re-)encrypts them with a new master_key, # or with their current master_key if a new one is not given. @@ -257,12 +276,14 @@ def rewrap_many_data_key(filter, opts = {}) @encryption_io, filter, master_key_document - ).run_state_machine + ).run_state_machine(timeout_holder) return RewrapManyDataKeyResult.new(nil) if rewrap_result.nil? updates = updates_from_data_key_documents(rewrap_result.fetch('v')) - RewrapManyDataKeyResult.new(@encryption_io.update_data_keys(updates)) + RewrapManyDataKeyResult.new( + @encryption_io.update_data_keys(updates, timeout_ms: @timeout_ms) + ) end private @@ -318,6 +339,14 @@ def updates_from_data_key_documents(documents) } end end + + def timeout_holder + CsotTimeoutHolder.new( + operation_timeouts: { + operation_timeout_ms: @timeout_ms + } + ) + end end end end diff --git a/lib/mongo/crypt/explicit_encryption_context.rb b/lib/mongo/crypt/explicit_encryption_context.rb index 2a737e0471..1207073d8e 100644 --- a/lib/mongo/crypt/explicit_encryption_context.rb +++ b/lib/mongo/crypt/explicit_encryption_context.rb @@ -39,27 +39,28 @@ class ExplicitEncryptionContext < Context # that will be used to encrypt the value. # @option options [ String ] :algorithm The algorithm used to encrypt the # value. Valid algorithms are "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - # "AEAD_AES_256_CBC_HMAC_SHA_512-Random", "Indexed", "Unindexed", "RangePreview". + # "AEAD_AES_256_CBC_HMAC_SHA_512-Random", "Indexed", "Unindexed", "Range". # @option options [ Integer | nil ] :contention_factor Contention factor # to be applied if encryption algorithm is set to "Indexed". If not # provided, it defaults to a value of 0. Contention factor should be set # only if encryption algorithm is set to "Indexed". # @option options [ String | nil ] query_type Query type to be applied - # if encryption algorithm is set to "Indexed" or "RangePreview". - # Allowed values are "equality" and "rangePreview". + # if encryption algorithm is set to "Indexed" or "Range". + # Allowed values are "equality" and "range". # @option options [ Hash | nil ] :range_opts Specifies index options for - # a Queryable Encryption field supporting "rangePreview" queries. + # a Queryable Encryption field supporting "range" queries. # Allowed options are: # - :min # - :max + # - :trim_factor # - :sparsity # - :precision - # min, max, sparsity, and range must match the values set in + # min, max, trim_factor, sparsity, and precision must match the values set in # the encryptedFields of the destination collection. # For double and decimal128, min/max/precision must all be set, # or all be unset. # - # @note The RangePreview algorithm is experimental only. It is not intended for + # @note The Range algorithm is experimental only. It is not intended for # public use. # # @raise [ ArgumentError|Mongo::Error::CryptError ] If invalid options are provided @@ -116,7 +117,7 @@ def set_key_alt_name(key_alt_name) def set_algorithm_opts(options) Binding.ctx_setopt_algorithm(self, options[:algorithm]) - if %w(Indexed RangePreview).include?(options[:algorithm]) + if %w(Indexed Range).include?(options[:algorithm]) if options[:contention_factor] Binding.ctx_setopt_contention_factor(self, options[:contention_factor]) end @@ -125,20 +126,25 @@ def set_algorithm_opts(options) end else if options[:contention_factor] - raise ArgumentError.new(':contention_factor is allowed only for "Indexed" or "RangePreview" algorithms') + raise ArgumentError.new(':contention_factor is allowed only for "Indexed" or "Range" algorithms') end if options[:query_type] - raise ArgumentError.new(':query_type is allowed only for "Indexed" or "RangePreview" algorithms') + raise ArgumentError.new(':query_type is allowed only for "Indexed" or "Range" algorithms') end end - if options[:algorithm] == 'RangePreview' + if options[:algorithm] == 'Range' Binding.ctx_setopt_algorithm_range(self, convert_range_opts(options[:range_opts])) end end def convert_range_opts(range_opts) range_opts.dup.tap do |opts| - opts[:sparsity] = BSON::Int64.new(opts[:sparsity]) unless opts[:sparsity].is_a?(BSON::Int64) + if opts[:sparsity] && !opts[:sparsity].is_a?(BSON::Int64) + opts[:sparsity] = BSON::Int64.new(opts[:sparsity]) + end + if opts[:trim_factor] + opts[:trimFactor] = opts.delete(:trim_factor) + end end end end diff --git a/lib/mongo/crypt/kms/azure/credentials_retriever.rb b/lib/mongo/crypt/kms/azure/credentials_retriever.rb index 88787d4409..c1b6898fa9 100644 --- a/lib/mongo/crypt/kms/azure/credentials_retriever.rb +++ b/lib/mongo/crypt/kms/azure/credentials_retriever.rb @@ -34,13 +34,16 @@ class CredentialsRetriever # request. This is used for testing. # @param [String | nil] metadata_host Azure metadata host. This # is used for testing. + # @param [ CsotTimeoutHolder | nil ] timeout_holder CSOT timeout. # # @return [ KMS::Azure::AccessToken ] Azure access token. # # @raise [KMS::CredentialsNotFound] If credentials could not be found. - def self.fetch_access_token(extra_headers: {}, metadata_host: nil) + # @raise Error::TimeoutError if credentials cannot be retrieved within + # the timeout. + def self.fetch_access_token(extra_headers: {}, metadata_host: nil, timeout_holder: nil) uri, req = prepare_request(extra_headers, metadata_host) - parsed_response = fetch_response(uri, req) + parsed_response = fetch_response(uri, req, timeout_holder) Azure::AccessToken.new( parsed_response.fetch('access_token'), Integer(parsed_response.fetch('expires_in')) @@ -78,13 +81,16 @@ def self.prepare_request(extra_headers, metadata_host) # # @param [URI] uri URI to Azure metadata host. # @param [Net::HTTP::Get] req Request object. + # @param [ CsotTimeoutHolder | nil ] timeout_holder CSOT timeout. # # @return [Hash] Parsed response. # # @raise [KMS::CredentialsNotFound] If cannot fetch response or # response is invalid. - def self.fetch_response(uri, req) - resp = do_request(uri, req) + # @raise Error::TimeoutError if credentials cannot be retrieved within + # the timeout. + def self.fetch_response(uri, req, timeout_holder) + resp = do_request(uri, req, timeout_holder) if resp.code != '200' raise KMS::CredentialsNotFound, "Azure metadata host responded with code #{resp.code}" @@ -100,12 +106,22 @@ def self.fetch_response(uri, req) # # @param [URI] uri URI to Azure metadata host. # @param [Net::HTTP::Get] req Request object. + # @param [ CsotTimeoutHolder | nil ] timeout_holder CSOT timeout. # # @return [Net::HTTPResponse] Response object. # # @raise [KMS::CredentialsNotFound] If cannot execute request. - def self.do_request(uri, req) - ::Timeout.timeout(10) do + # @raise Error::TimeoutError if credentials cannot be retrieved within + # the timeout. + def self.do_request(uri, req, timeout_holder) + timeout_holder&.check_timeout! + timeout = timeout_holder&.remaining_timeout_sec || 10 + exception_class = if timeout_holder&.csot? + Error::TimeoutError + else + nil + end + ::Timeout.timeout(timeout, exception_class) do Net::HTTP.start(uri.hostname, uri.port, use_ssl: false) do |http| http.request(req) end diff --git a/lib/mongo/crypt/kms/gcp/credentials_retriever.rb b/lib/mongo/crypt/kms/gcp/credentials_retriever.rb index 723cda43c4..bbb2887fbd 100644 --- a/lib/mongo/crypt/kms/gcp/credentials_retriever.rb +++ b/lib/mongo/crypt/kms/gcp/credentials_retriever.rb @@ -29,14 +29,20 @@ class CredentialsRetriever DEFAULT_HOST = 'metadata.google.internal' - def self.fetch_access_token + # Fetch GCP access token. + # + # @param [ CsotTimeoutHolder | nil ] timeout_holder CSOT timeout. + # + # @return [ String ] GCP access token. + # + # @raise [ KMS::CredentialsNotFound ] + # @raise [ Error::TimeoutError ] + def self.fetch_access_token(timeout_holder = nil) host = ENV.fetch(METADATA_HOST_ENV) { DEFAULT_HOST } uri = URI("http://#{host}/computeMetadata/v1/instance/service-accounts/default/token") req = Net::HTTP::Get.new(uri) req['Metadata-Flavor'] = 'Google' - resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: false) do |http| - http.request(req) - end + resp = fetch_response(uri, req, timeout_holder) if resp.code != '200' raise KMS::CredentialsNotFound, "GCE metadata host responded with code #{resp.code}" @@ -50,6 +56,25 @@ def self.fetch_access_token raise KMS::CredentialsNotFound, "Could not receive GCP metadata response; #{e.class}: #{e.message}" end + + def self.fetch_response(uri, req, timeout_holder) + timeout_holder&.check_timeout! + if timeout_holder&.timeout? + ::Timeout.timeout(timeout_holder.remaining_timeout_sec, Error:TimeoutError) do + do_fetch(uri, req) + end + else + do_fetch(uri, req) + end + end + private_class_method :fetch_response + + def self.do_fetch(uri, req) + Net::HTTP.start(uri.hostname, uri.port, use_ssl: false) do |http| + http.request(req) + end + end + private_class_method :do_fetch end end end diff --git a/lib/mongo/csot_timeout_holder.rb b/lib/mongo/csot_timeout_holder.rb new file mode 100644 index 0000000000..9d7d15c0a0 --- /dev/null +++ b/lib/mongo/csot_timeout_holder.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +# Copyright (C) 2024 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. + +module Mongo + # This class stores operation timeout and provides corresponding helper methods. + # + # @api private + class CsotTimeoutHolder + def initialize(session: nil, operation_timeouts: {}) + @deadline = calculate_deadline(operation_timeouts, session) + @operation_timeouts = operation_timeouts + @timeout_sec = (@deadline - Utils.monotonic_time if @deadline) + end + + attr_reader :deadline, :timeout_sec, :operation_timeouts + + # @return [ true | false ] Whether CSOT is enabled for the operation + def csot? + !deadline.nil? + end + + # @return [ true | false ] Returns false if CSOT is not enabled, or if + # CSOT is set to 0 (means unlimited), otherwise true. + def timeout? + ![ nil, 0 ].include?(@deadline) + end + + # @return [ Float | nil ] Returns the remaining seconds of the timeout + # set for the operation; if no timeout is set, or the timeout is 0 + # (means unlimited) returns nil. + def remaining_timeout_sec + return nil unless timeout? + + deadline - Utils.monotonic_time + end + + def remaining_timeout_sec! + check_timeout! + remaining_timeout_sec + end + + # @return [ Integer | nil ] Returns the remaining milliseconds of the timeout + # set for the operation; if no timeout is set, or the timeout is 0 + # (means unlimited) returns nil. + def remaining_timeout_ms + seconds = remaining_timeout_sec + return nil if seconds.nil? + + (seconds * 1_000).to_i + end + + def remaining_timeout_ms! + check_timeout! + remaining_timeout_ms + end + + # @return [ true | false ] Whether the timeout for the operation expired. + # If no timeout set, this method returns false. + def timeout_expired? + if timeout? + Utils.monotonic_time >= deadline + else + false + end + end + + # Check whether the operation timeout expired, and raises an appropriate + # error if yes. + # + # @raise [ Error::TimeoutError ] + def check_timeout! + return unless timeout_expired? + + raise Error::TimeoutError, "Operation took more than #{timeout_sec} seconds" + end + + private + + def calculate_deadline(opts = {}, session = nil) + check_no_override_inside_transaction!(opts, session) + return session&.with_transaction_deadline if session&.with_transaction_deadline + + if (operation_timeout_ms = opts[:operation_timeout_ms]) + calculate_deadline_from_timeout_ms(operation_timeout_ms) + elsif (inherited_timeout_ms = opts[:inherited_timeout_ms]) + calculate_deadline_from_timeout_ms(inherited_timeout_ms) + end + end + + def check_no_override_inside_transaction!(opts, session) + return unless opts[:operation_timeout_ms] && session&.with_transaction_deadline + + raise ArgumentError, 'Cannot override timeout_ms inside with_transaction block' + end + + def calculate_deadline_from_timeout_ms(operation_timeout_ms) + if operation_timeout_ms.positive? + Utils.monotonic_time + (operation_timeout_ms / 1_000.0) + elsif operation_timeout_ms.zero? + 0 + elsif operation_timeout_ms.negative? + raise ArgumentError, "timeout_ms must be a non-negative integer but #{operation_timeout_ms} given" + end + end + end +end diff --git a/lib/mongo/cursor.rb b/lib/mongo/cursor.rb index d24e137b5b..14a486a713 100644 --- a/lib/mongo/cursor.rb +++ b/lib/mongo/cursor.rb @@ -49,6 +49,9 @@ class Cursor # @api private attr_reader :resume_token + # @return [ Operation::Context ] context the context for this cursor + attr_reader :context + # Creates a +Cursor+ object. # # @example Instantiate the cursor. @@ -59,6 +62,8 @@ class Cursor # @param [ Server ] server The server this cursor is locked to. # @param [ Hash ] options The cursor options. # + # @option options [ Operation::Context ] :context The operation context + # for this cursor. # @option options [ true, false ] :disable_retry Whether to disable # retrying on error when sending getMore operations (deprecated, getMore # operations are no longer retried) @@ -80,15 +85,25 @@ def initialize(view, result, server, options = {}) if @cursor_id.nil? raise ArgumentError, 'Cursor id must be present in the result' end - @connection_global_id = result.connection_global_id @options = options @session = @options[:session] + @connection_global_id = result.connection_global_id + @context = @options[:context]&.with(connection_global_id: connection_global_id_for_context) || fresh_context @explicitly_closed = false @lock = Mutex.new - unless closed? + if server.load_balancer? + # We need the connection in the cursor only in load balanced topology; + # we do not need an additional reference to it otherwise. + @connection = @initial_result.connection + end + if closed? + check_in_connection + else register - ObjectSpace.define_finalizer(self, self.class.finalize(kill_spec(@connection_global_id), - cluster)) + ObjectSpace.define_finalizer( + self, + self.class.finalize(kill_spec(@connection_global_id), cluster) + ) end end @@ -98,6 +113,9 @@ def initialize(view, result, server, options = {}) # @api private attr_reader :initial_result + # @api private + attr_reader :connection + # Finalize the cursor for garbage collection. Schedules this cursor to be included # in a killCursors operation executed by the Cluster's CursorReaper. # @@ -284,9 +302,11 @@ def closed? # the server. # # @return [ nil ] Always nil. - def close + def close(opts = {}) return if closed? + ctx = context ? context.refresh(timeout_ms: opts[:timeout_ms]) : fresh_context(opts) + unregister read_with_one_retry do spec = { @@ -295,11 +315,11 @@ def close cursor_ids: [id], } op = Operation::KillCursors.new(spec) - execute_operation(op) + execute_operation(op, context: ctx) end nil - rescue Error::OperationFailure, Error::SocketError, Error::SocketTimeoutError, Error::ServerNotUsable + rescue Error::OperationFailure::Family, Error::SocketError, Error::SocketTimeoutError, Error::ServerNotUsable # Errors are swallowed since there is noting can be done by handling them. ensure end_session @@ -307,6 +327,7 @@ def close @lock.synchronize do @explicitly_closed = true end + check_in_connection end # Get the parsed collection name. @@ -387,6 +408,7 @@ def kill_spec(connection_global_id) connection_global_id: connection_global_id, server_address: server.address, session: @session, + connection: @connection ) end @@ -434,15 +456,7 @@ def get_more_operation # 3.2+ servers use batch_size, 3.0- servers use to_return. # TODO should to_return be calculated in the operation layer? batch_size: batch_size_for_get_more, - to_return: to_return, - max_time_ms: if view.respond_to?(:max_await_time_ms) && - view.max_await_time_ms && - view.options[:await_data] - then - view.max_await_time_ms - else - nil - end, + to_return: to_return } if view.respond_to?(:options) && view.options.is_a?(Hash) spec[:comment] = view.options[:comment] unless view.options[:comment].nil? @@ -464,7 +478,10 @@ def process(result) # the @cursor_id may be zero (all results fit in the first batch). # Thus we need to check both @cursor_id and the cursor_id of the result # prior to calling unregister here. - unregister if !closed? && result.cursor_id == 0 + if !closed? && result.cursor_id == 0 + unregister + check_in_connection + end @cursor_id = set_cursor_id(result) if result.respond_to?(:post_batch_resume_token) @@ -495,13 +512,22 @@ def unregister cluster.unregister_cursor(@cursor_id) end - def execute_operation(op) - context = Operation::Context.new( - client: client, - session: @session, - connection_global_id: @connection_global_id, - ) - op.execute(@server, context: context) + def execute_operation(op, context: nil) + op_context = context || possibly_refreshed_context + if @connection.nil? + op.execute(@server, context: op_context) + else + op.execute_with_connection(@connection, context: op_context) + end + end + + # Considers the timeout mode and will either return the cursor's + # context directly, or will return a new (refreshed) context. + # + # @return [ Operation::Context ] the (possibly-refreshed) context. + def possibly_refreshed_context + return context if view.timeout_mode == :cursor_lifetime + context.refresh(view: view) end # Sets @cursor_id from the operation result. @@ -521,6 +547,42 @@ def set_cursor_id(result) end end + # Returns a newly instantiated operation context based on the + # default values from the view. + def fresh_context(opts = {}) + Operation::Context.new(client: view.client, + session: @session, + connection_global_id: connection_global_id_for_context, + operation_timeouts: view.operation_timeouts(opts), + view: view) + end + + # Because a context must not have a connection_global_id if the session + # is already pinned to one, this method checks to see whether or not there's + # pinned connection_global_id on the session and returns nil if so. + def connection_global_id_for_context + if @session&.pinned_connection_global_id + nil + else + @connection_global_id + end + end + + # Returns the connection that was used to create the cursor back to the + # corresponding connection pool. + # + # In a load balanced topology cursors must use the same connection for the + # initial and all subsequent operations. Therefore, the connection is not + # checked into the pool after the initial operation is completed, but + # only when the cursor is drained. + def check_in_connection + # Connection nil means the connection has been already checked in. + return if @connection.nil? + return unless @connection.server.load_balancer? + + @connection.connection_pool.check_in(@connection) + @connection = nil + end end end diff --git a/lib/mongo/cursor/kill_spec.rb b/lib/mongo/cursor/kill_spec.rb index 117f1b50fa..09047ba371 100644 --- a/lib/mongo/cursor/kill_spec.rb +++ b/lib/mongo/cursor/kill_spec.rb @@ -31,7 +31,8 @@ def initialize( db_name:, connection_global_id:, server_address:, - session: + session:, + connection: nil ) @cursor_id = cursor_id @coll_name = coll_name @@ -39,6 +40,7 @@ def initialize( @connection_global_id = connection_global_id @server_address = server_address @session = session + @connection = connection end attr_reader :cursor_id, @@ -46,7 +48,8 @@ def initialize( :db_name, :connection_global_id, :server_address, - :session + :session, + :connection def ==(other) cursor_id == other.cursor_id && diff --git a/lib/mongo/cursor/nontailable.rb b/lib/mongo/cursor/nontailable.rb new file mode 100644 index 0000000000..d28863d3cc --- /dev/null +++ b/lib/mongo/cursor/nontailable.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Mongo + class Cursor + # This module is used by cursor-implementing classes to indicate that + # the only cursors they generate are non-tailable, and iterable. + # + # @api private + module NonTailable + # These views are always non-tailable. + # + # @return [ nil ] indicating a non-tailable cursor. + def cursor_type + nil + end + + # These views apply timeouts to each iteration of a cursor, as + # opposed to the entire lifetime of the cursor. + # + # @return [ :iterable ] indicating a cursor with a timeout mode of + # "iterable". + def timeout_mode + :iterable + end + end + end +end diff --git a/lib/mongo/cursor_host.rb b/lib/mongo/cursor_host.rb new file mode 100644 index 0000000000..1d192527d9 --- /dev/null +++ b/lib/mongo/cursor_host.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +module Mongo + # A shared concern implementing settings and configuration for entities that + # "host" (or spawn) cursors. + # + # The class or module that includes this concern must implement: + # * timeout_ms -- this must return either the operation level timeout_ms + # (if set) or an inherited timeout_ms from a hierarchically higher + # level (if any). + module CursorHost + # Returns the cursor associated with this view, if any. + # + # @return [ nil | Cursor ] The cursor, if any. + # + # @api private + attr_reader :cursor + + # @return [ :cursor_lifetime | :iteration ] The timeout mode to be + # used by this object. + attr_reader :timeout_mode + + # Ensure the timeout mode is appropriate for other options that + # have been given. + # + # @param [ Hash ] options The options to inspect. + # @param [ Array ] forbid The list of options to forbid for this + # class. + # + # @raise [ ArgumentError ] if inconsistent or incompatible options are + # detected. + # + # @api private + # rubocop:disable Metrics + def validate_timeout_mode!(options, forbid: []) + forbid.each do |key| + raise ArgumentError, "#{key} is not allowed here" if options.key?(key) + end + + cursor_type = options[:cursor_type] + timeout_mode = options[:timeout_mode] + + if timeout_ms + # "Tailable cursors only support the ITERATION value for the + # timeoutMode option. This is the default value and drivers MUST + # error if the option is set to CURSOR_LIFETIME." + if cursor_type + timeout_mode ||= :iteration + if timeout_mode == :cursor_lifetime + raise ArgumentError, 'tailable cursors only support `timeout_mode: :iteration`' + end + + # "Drivers MUST error if [the maxAwaitTimeMS] option is set, + # timeoutMS is set to a non-zero value, and maxAwaitTimeMS is + # greater than or equal to timeoutMS." + max_await_time_ms = options[:max_await_time_ms] || 0 + if cursor_type == :tailable_await && max_await_time_ms >= timeout_ms + raise ArgumentError, ':max_await_time_ms must not be >= :timeout_ms' + end + else + # "For non-tailable cursors, the default value of timeoutMode + # is CURSOR_LIFETIME." + timeout_mode ||= :cursor_lifetime + end + elsif timeout_mode + # "Drivers MUST error if timeoutMode is set and timeoutMS is not." + raise ArgumentError, ':timeout_ms must be set if :timeout_mode is set' + end + + if timeout_mode == :iteration && respond_to?(:write?) && write? + raise ArgumentError, 'timeout_mode=:iteration is not supported for aggregation pipelines with $out or $merge' + end + + # set it as an instance variable, rather than updating the options, + # because if the cursor type changes (e.g. via #configure()), the new + # View instance must be able to select a different default timeout_mode + # if no timeout_mode was set initially. + @timeout_mode = timeout_mode + end + # rubocop:enable Metrics + end +end diff --git a/lib/mongo/database.rb b/lib/mongo/database.rb index 9b656c5b99..5fb69bc09a 100644 --- a/lib/mongo/database.rb +++ b/lib/mongo/database.rb @@ -128,6 +128,10 @@ def [](collection_name, options = {}) # required privilege to run the command when access control is enforced # @option options [ Object ] :comment A user-provided # comment to attach to this command. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the database or the client. # # See https://mongodb.com/docs/manual/reference/command/listCollections/ # for more information and usage. @@ -136,7 +140,7 @@ def [](collection_name, options = {}) # # @since 2.0.0 def collection_names(options = {}) - View.new(self).collection_names(options) + View.new(self, options).collection_names(options) end # Get info on all the non-system collections in the database. @@ -156,6 +160,10 @@ def collection_names(options = {}) # required privilege to run the command when access control is enforced. # @option options [ Object ] :comment A user-provided # comment to attach to this command. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the database or the client. # # See https://mongodb.com/docs/manual/reference/command/listCollections/ # for more information and usage. @@ -165,7 +173,7 @@ def collection_names(options = {}) # # @since 2.0.5 def list_collections(options = {}) - View.new(self).list_collections(options) + View.new(self, options).list_collections(options) end # Get all the non-system collections that belong to this database. @@ -181,6 +189,10 @@ def list_collections(options = {}) # required privilege to run the command when access control is enforced. # @option options [ Object ] :comment A user-provided # comment to attach to this command. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the database or the client. # # See https://mongodb.com/docs/manual/reference/command/listCollections/ # for more information and usage. @@ -202,6 +214,10 @@ def collections(options = {}) # # @option opts :read [ Hash ] The read preference for this command. # @option opts :session [ Session ] The session to use for this command. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the database or the client. # @option opts :execution_options [ Hash ] Options to pass to the code that # executes this command. This is an internal option and is subject to # change. @@ -223,7 +239,7 @@ def command(operation, opts = {}) Lint.validate_underscore_read_preference(txn_read_pref) selector = ServerSelector.get(txn_read_pref) - client.send(:with_session, opts) do |session| + client.with_session(opts) do |session| server = selector.select_server(cluster, nil, session) op = Operation::Command.new( :selector => operation, @@ -233,7 +249,11 @@ def command(operation, opts = {}) ) op.execute(server, - context: Operation::Context.new(client: client, session: session), + context: Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(opts) + ), options: execution_opts) end end @@ -245,6 +265,12 @@ def command(operation, opts = {}) # # @option opts :read [ Hash ] The read preference for this command. # @option opts :session [ Session ] The session to use for this command. + # @option opts [ Object ] :comment A user-provided + # comment to attach to this command. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the database or the client. # # @return [ Hash ] The result of the command execution. # @api private @@ -258,15 +284,20 @@ def read_command(operation, opts = {}) Lint.validate_underscore_read_preference(txn_read_pref) preference = ServerSelector.get(txn_read_pref) - client.send(:with_session, opts) do |session| - read_with_retry(session, preference) do |server| + client.with_session(opts) do |session| + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(opts) + ) + read_with_retry(session, preference, context) do |server| Operation::Command.new( selector: operation.dup, db_name: name, read: preference, session: session, comment: opts[:comment], - ).execute(server, context: Operation::Context.new(client: client, session: session)) + ).execute(server, context: context) end end end @@ -279,14 +310,18 @@ def read_command(operation, opts = {}) # @param [ Hash ] options The options for the operation. # # @option options [ Session ] :session The session to use for the operation. - # @option opts [ Hash ] :write_concern The write concern options. + # @option options [ Hash ] :write_concern The write concern options. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the database or the client. # # @return [ Result ] The result of the command. # # @since 2.0.0 def drop(options = {}) operation = { :dropDatabase => 1 } - client.send(:with_session, options) do |session| + client.with_session(options) do |session| write_concern = if options[:write_concern] WriteConcern.get(options[:write_concern]) else @@ -297,7 +332,14 @@ def drop(options = {}) db_name: name, write_concern: write_concern, session: session - }).execute(next_primary(nil, session), context: Operation::Context.new(client: client, session: session)) + }).execute( + next_primary(nil, session), + context: Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(options) + ) + ) end end @@ -309,6 +351,10 @@ def drop(options = {}) # @param [ Mongo::Client ] client The driver client. # @param [ String, Symbol ] name The name of the database. # @param [ Hash ] options The options. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the client. # # @raise [ Mongo::Database::InvalidName ] If the name is nil. # @@ -388,20 +434,21 @@ def users # @option options [ Hash ] :collation The collation to use. # @option options [ Object ] :comment A user-provided # comment to attach to this command. + # @option options [ Integer ] :max_time_ms The maximum amount of time to + # allow the query to run, in milliseconds. This option is deprecated, use + # :timeout_ms instead. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the database or the client. # @option options [ String ] :hint The index to use for the aggregation. - # @option options [ Integer ] :max_time_ms The maximum amount of time in - # milliseconds to allow the aggregation to run. - # @option options [ true, false ] :use_cursor Indicates whether the command - # will request that the server provide results using a cursor. Note that - # as of server version 3.6, aggregations always provide results using a - # cursor and this option is therefore not valid. # @option options [ Session ] :session The session to use. # # @return [ Collection::View::Aggregation ] The aggregation object. # # @since 2.10.0 def aggregate(pipeline, options = {}) - View.new(self).aggregate(pipeline, options) + View.new(self, options).aggregate(pipeline, options) end # As of version 3.6 of the MongoDB server, a ``$changeStream`` pipeline stage is supported @@ -471,7 +518,7 @@ def aggregate(pipeline, options = {}) # @since 2.6.0 def watch(pipeline = [], options = {}) view_options = options.dup - view_options[:await_data] = true if options[:max_await_time_ms] + view_options[:cursor_type] = :tailable_await if options[:max_await_time_ms] Mongo::Collection::View::ChangeStream.new( Mongo::Collection::View.new(collection("#{COMMAND}.aggregate"), {}, view_options), @@ -497,5 +544,28 @@ def self.create(client) database = Database.new(client, client.options[:database], client.options) client.instance_variable_set(:@database, database) end + + # @return [ Integer | nil ] Operation timeout that is for this database or + # for the corresponding client. + # + # @api private + def timeout_ms + options[:timeout_ms] || client.timeout_ms + end + + # @return [ Hash ] timeout_ms value set on the operation level (if any), + # and/or timeout_ms that is set on collection/database/client level (if any). + # + # @api private + def operation_timeouts(opts) + # TODO: We should re-evaluate if we need two timeouts separately. + {}.tap do |result| + if opts[:timeout_ms].nil? + result[:inherited_timeout_ms] = timeout_ms + else + result[:operation_timeout_ms] = opts.delete(:timeout_ms) + end + end + end end end diff --git a/lib/mongo/database/view.rb b/lib/mongo/database/view.rb index 4e8195fb67..3c0bcaaf89 100644 --- a/lib/mongo/database/view.rb +++ b/lib/mongo/database/view.rb @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'mongo/cursor/nontailable' + module Mongo class Database @@ -25,6 +27,8 @@ class View extend Forwardable include Enumerable include Retryable + include Mongo::CursorHost + include Cursor::NonTailable def_delegators :@database, :cluster, :read_preference, :client # @api private @@ -56,6 +60,10 @@ class View # to run the command when access control is enforced. # @option options [ Object ] :comment A user-provided # comment to attach to this command. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the database or the client. # # See https://mongodb.com/docs/manual/reference/command/listCollections/ # for more information and usage. @@ -66,9 +74,14 @@ class View # @since 2.0.0 def collection_names(options = {}) @batch_size = options[:batch_size] - session = client.send(:get_session, options) - cursor = read_with_retry_cursor(session, ServerSelector.primary, self) do |server| - send_initial_query(server, session, options.merge(name_only: true)) + session = client.get_session(options) + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(options) + ) + cursor = read_with_retry_cursor(session, ServerSelector.primary, self, context: context) do |server| + send_initial_query(server, session, context, options.merge(name_only: true)) end cursor.map do |info| if cursor.initial_result.connection_description.features.list_collections_enabled? @@ -112,20 +125,33 @@ def collection_names(options = {}) # # @since 2.0.5 def list_collections(options = {}) - session = client.send(:get_session, options) + session = client.get_session(options) collections_info(session, ServerSelector.primary, options) end # Create the new database view. # # @example Create the new database view. - # View::Index.new(database) + # Database::View.new(database) # # @param [ Database ] database The database. + # @param [ Hash ] options The options to configure the view with. + # + # @option options [ :cursor_lifetime | :iteration ] :timeout_mode How to interpret + # :timeout_ms (whether it applies to the lifetime of the cursor, or per + # iteration). + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the database or the client. # # @since 2.0.0 - def initialize(database) + def initialize(database, options = {}) @database = database + @operation_timeout_ms = options.delete(:timeout_ms) + + validate_timeout_mode!(options) + @batch_size = nil @limit = nil @collection = @database[Database::COMMAND] @@ -134,6 +160,12 @@ def initialize(database) # @api private attr_reader :database + # @return [ Integer | nil | The timeout_ms value that was passed as an + # option to the view. + # + # @api private + attr_reader :operation_timeout_ms + # Execute an aggregation on the database view. # # @example Aggregate documents. @@ -152,15 +184,41 @@ def aggregate(pipeline, options = {}) Collection::View::Aggregation.new(self, pipeline, options) end + # The timeout_ms value to use for this operation; either specified as an + # option to the view, or inherited from the database. + # + # @return [ Integer | nil ] the timeout_ms for this operation + def timeout_ms + operation_timeout_ms || database.timeout_ms + end + + # @return [ Hash ] timeout_ms value set on the operation level (if any). + # + # @api private + def operation_timeouts(opts = {}) + {}.tap do |result| + if opts[:timeout_ms] || operation_timeout_ms + result[:operation_timeout_ms] = opts.delete(:timeout_ms) || operation_timeout_ms + else + result[:inherited_timeout_ms] = database.timeout_ms + end + end + end + private def collections_info(session, server_selector, options = {}, &block) description = nil - cursor = read_with_retry_cursor(session, server_selector, self) do |server| + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(options) + ) + cursor = read_with_retry_cursor(session, server_selector, self, context: context) do |server| # TODO take description from the connection used to send the query # once https://jira.mongodb.org/browse/RUBY-1601 is fixed. description = server.description - send_initial_query(server, session, options) + send_initial_query(server, session, context, options) end # On 3.0+ servers, we get just the collection names. # On 2.6 server, we get collection names prefixed with the database @@ -224,17 +282,26 @@ def initial_query_op(session, options = {}) # types (where possible). # # @return [ Operation::Result ] Result of the query. - def send_initial_query(server, session, options = {}) + def send_initial_query(server, session, context, options = {}) opts = options.dup execution_opts = {} if opts.key?(:deserialize_as_bson) execution_opts[:deserialize_as_bson] = opts.delete(:deserialize_as_bson) end - initial_query_op(session, opts).execute( - server, - context: Operation::Context.new(client: client, session: session), - options: execution_opts - ) + if server.load_balancer? + connection = server.pool.check_out(context: context) + initial_query_op(session, opts).execute_with_connection( + connection, + context: context, + options: execution_opts + ) + else + initial_query_op(session, opts).execute( + server, + context: context, + options: execution_opts + ) + end end end end diff --git a/lib/mongo/error.rb b/lib/mongo/error.rb index 92d6d5f4b3..8750301076 100644 --- a/lib/mongo/error.rb +++ b/lib/mongo/error.rb @@ -217,7 +217,9 @@ def write_concern_error_labels require 'mongo/error/server_api_conflict' require 'mongo/error/server_api_not_supported' require 'mongo/error/server_not_usable' +require 'mongo/error/server_timeout_error' require 'mongo/error/transactions_not_supported' +require 'mongo/error/timeout_error' require 'mongo/error/unknown_payload_type' require 'mongo/error/unmet_dependency' require 'mongo/error/unsupported_option' diff --git a/lib/mongo/error/operation_failure.rb b/lib/mongo/error/operation_failure.rb index 1cc47f520b..236eb7083f 100644 --- a/lib/mongo/error/operation_failure.rb +++ b/lib/mongo/error/operation_failure.rb @@ -18,242 +18,247 @@ module Mongo class Error - # Raised when an operation fails for some reason. - # - # @since 2.0.0 class OperationFailure < Error - extend Forwardable - include SdamErrorDetection - include ReadWriteRetryable + # Implements the behavior for an OperationFailure error. Other errors + # (e.g. ServerTimeoutError) may also implement this, so that they may + # be recognized and treated as OperationFailure errors. + module OperationFailure::Family + extend Forwardable + include SdamErrorDetection + include ReadWriteRetryable - def_delegators :@result, :operation_time + def_delegators :@result, :operation_time - # @!method connection_description - # - # @return [ Server::Description ] Server description of the server that - # the operation that this exception refers to was performed on. - # - # @api private - def_delegator :@result, :connection_description + # @!method connection_description + # + # @return [ Server::Description ] Server description of the server that + # the operation that this exception refers to was performed on. + # + # @api private + def_delegator :@result, :connection_description - # @return [ Integer ] The error code parsed from the document. - # - # @since 2.6.0 - attr_reader :code + # @return [ Integer ] The error code parsed from the document. + # + # @since 2.6.0 + attr_reader :code - # @return [ String ] The error code name parsed from the document. - # - # @since 2.6.0 - attr_reader :code_name + # @return [ String ] The error code name parsed from the document. + # + # @since 2.6.0 + attr_reader :code_name - # @return [ String ] The server-returned error message - # parsed from the response. - # - # @api experimental - attr_reader :server_message + # @return [ String ] The server-returned error message + # parsed from the response. + # + # @api experimental + attr_reader :server_message - # Error codes and code names that should result in a failing getMore - # command on a change stream NOT being resumed. - # - # @api private - CHANGE_STREAM_RESUME_ERRORS = [ - {code_name: 'HostUnreachable', code: 6}, - {code_name: 'HostNotFound', code: 7}, - {code_name: 'NetworkTimeout', code: 89}, - {code_name: 'ShutdownInProgress', code: 91}, - {code_name: 'PrimarySteppedDown', code: 189}, - {code_name: 'ExceededTimeLimit', code: 262}, - {code_name: 'SocketException', code: 9001}, - {code_name: 'NotMaster', code: 10107}, - {code_name: 'InterruptedAtShutdown', code: 11600}, - {code_name: 'InterruptedDueToReplStateChange', code: 11602}, - {code_name: 'NotPrimaryNoSecondaryOk', code: 13435}, - {code_name: 'NotMasterOrSecondary', code: 13436}, + # Error codes and code names that should result in a failing getMore + # command on a change stream NOT being resumed. + # + # @api private + CHANGE_STREAM_RESUME_ERRORS = [ + {code_name: 'HostUnreachable', code: 6}, + {code_name: 'HostNotFound', code: 7}, + {code_name: 'NetworkTimeout', code: 89}, + {code_name: 'ShutdownInProgress', code: 91}, + {code_name: 'PrimarySteppedDown', code: 189}, + {code_name: 'ExceededTimeLimit', code: 262}, + {code_name: 'SocketException', code: 9001}, + {code_name: 'NotMaster', code: 10107}, + {code_name: 'InterruptedAtShutdown', code: 11600}, + {code_name: 'InterruptedDueToReplStateChange', code: 11602}, + {code_name: 'NotPrimaryNoSecondaryOk', code: 13435}, + {code_name: 'NotMasterOrSecondary', code: 13436}, - {code_name: 'StaleShardVersion', code: 63}, - {code_name: 'FailedToSatisfyReadPreference', code: 133}, - {code_name: 'StaleEpoch', code: 150}, - {code_name: 'RetryChangeStream', code: 234}, - {code_name: 'StaleConfig', code: 13388}, - ].freeze + {code_name: 'StaleShardVersion', code: 63}, + {code_name: 'FailedToSatisfyReadPreference', code: 133}, + {code_name: 'StaleEpoch', code: 150}, + {code_name: 'RetryChangeStream', code: 234}, + {code_name: 'StaleConfig', code: 13388}, + ].freeze - # Change stream can be resumed when these error messages are encountered. - # - # @since 2.6.0 - # @api private - CHANGE_STREAM_RESUME_MESSAGES = ReadWriteRetryable::WRITE_RETRY_MESSAGES + # Change stream can be resumed when these error messages are encountered. + # + # @since 2.6.0 + # @api private + CHANGE_STREAM_RESUME_MESSAGES = ReadWriteRetryable::WRITE_RETRY_MESSAGES - # Can the change stream on which this error occurred be resumed, - # provided the operation that triggered this error was a getMore? - # - # @example Is the error resumable for the change stream? - # error.change_stream_resumable? - # - # @return [ true, false ] Whether the error is resumable. - # - # @since 2.6.0 - def change_stream_resumable? - if @result && @result.is_a?(Mongo::Operation::GetMore::Result) - # CursorNotFound exceptions are always resumable because the server - # is not aware of the cursor id, and thus cannot determine if - # the cursor is a change stream and cannot add the - # ResumableChangeStreamError label. - return true if code == 43 + # Can the change stream on which this error occurred be resumed, + # provided the operation that triggered this error was a getMore? + # + # @example Is the error resumable for the change stream? + # error.change_stream_resumable? + # + # @return [ true, false ] Whether the error is resumable. + # + # @since 2.6.0 + def change_stream_resumable? + if @result && @result.is_a?(Mongo::Operation::GetMore::Result) + # CursorNotFound exceptions are always resumable because the server + # is not aware of the cursor id, and thus cannot determine if + # the cursor is a change stream and cannot add the + # ResumableChangeStreamError label. + return true if code == 43 - # Connection description is not populated for unacknowledged writes. - if connection_description.max_wire_version >= 9 - label?('ResumableChangeStreamError') + # Connection description is not populated for unacknowledged writes. + if connection_description.max_wire_version >= 9 + label?('ResumableChangeStreamError') + else + change_stream_resumable_code? + end else - change_stream_resumable_code? + false end - else - false end - end - def change_stream_resumable_code? - CHANGE_STREAM_RESUME_ERRORS.any? { |e| e[:code] == code } - end - private :change_stream_resumable_code? + def change_stream_resumable_code? + CHANGE_STREAM_RESUME_ERRORS.any? { |e| e[:code] == code } + end + private :change_stream_resumable_code? - # @return [ true | false ] Whether the failure includes a write - # concern error. A failure may have a top level error and a write - # concern error or either one of the two. - # - # @since 2.10.0 - def write_concern_error? - !!@write_concern_error_document - end + # @return [ true | false ] Whether the failure includes a write + # concern error. A failure may have a top level error and a write + # concern error or either one of the two. + # + # @since 2.10.0 + def write_concern_error? + !!@write_concern_error_document + end - # Returns the write concern error document as it was reported by the - # server, if any. - # - # @return [ Hash | nil ] Write concern error as reported to the server. - attr_reader :write_concern_error_document + # Returns the write concern error document as it was reported by the + # server, if any. + # + # @return [ Hash | nil ] Write concern error as reported to the server. + attr_reader :write_concern_error_document - # @return [ Integer | nil ] The error code for the write concern error, - # if a write concern error is present and has a code. - # - # @since 2.10.0 - attr_reader :write_concern_error_code + # @return [ Integer | nil ] The error code for the write concern error, + # if a write concern error is present and has a code. + # + # @since 2.10.0 + attr_reader :write_concern_error_code - # @return [ String | nil ] The code name for the write concern error, - # if a write concern error is present and has a code name. - # - # @since 2.10.0 - attr_reader :write_concern_error_code_name + # @return [ String | nil ] The code name for the write concern error, + # if a write concern error is present and has a code name. + # + # @since 2.10.0 + attr_reader :write_concern_error_code_name - # @return [ String | nil ] The details of the error. - # For WriteConcernErrors this is `document['writeConcernError']['errInfo']`. - # For WriteErrors this is `document['writeErrors'][0]['errInfo']`. - # For all other errors this is nil. - attr_reader :details + # @return [ String | nil ] The details of the error. + # For WriteConcernErrors this is `document['writeConcernError']['errInfo']`. + # For WriteErrors this is `document['writeErrors'][0]['errInfo']`. + # For all other errors this is nil. + attr_reader :details - # @return [ BSON::Document | nil ] The server-returned error document. - # - # @api experimental - attr_reader :document + # @return [ BSON::Document | nil ] The server-returned error document. + # + # @api experimental + attr_reader :document - # Create the operation failure. - # - # @example Create the error object - # OperationFailure.new(message, result) - # - # @example Create the error object with a code and a code name - # OperationFailure.new(message, result, :code => code, :code_name => code_name) - # - # @param [ String ] message The error message. - # @param [ Operation::Result ] result The result object. - # @param [ Hash ] options Additional parameters. - # - # @option options [ Integer ] :code Error code. - # @option options [ String ] :code_name Error code name. - # @option options [ BSON::Document ] :document The server-returned - # error document. - # @option options [ String ] server_message The server-returned - # error message parsed from the response. - # @option options [ Hash ] :write_concern_error_document The - # server-supplied write concern error document, if any. - # @option options [ Integer ] :write_concern_error_code Error code for - # write concern error, if any. - # @option options [ String ] :write_concern_error_code_name Error code - # name for write concern error, if any. - # @option options [ Array ] :write_concern_error_labels Error - # labels for the write concern error, if any. - # @option options [ Array ] :labels The set of labels associated - # with the error. - # @option options [ true | false ] :wtimeout Whether the error is a wtimeout. - def initialize(message = nil, result = nil, options = {}) - @details = retrieve_details(options[:document]) - super(append_details(message, @details)) + # @return [ Operation::Result ] the result object for the operation. + # + # @api private + attr_reader :result - @result = result - @code = options[:code] - @code_name = options[:code_name] - @write_concern_error_document = options[:write_concern_error_document] - @write_concern_error_code = options[:write_concern_error_code] - @write_concern_error_code_name = options[:write_concern_error_code_name] - @write_concern_error_labels = options[:write_concern_error_labels] || [] - @labels = options[:labels] || [] - @wtimeout = !!options[:wtimeout] - @document = options[:document] - @server_message = options[:server_message] - end + # Create the operation failure. + # + # @param [ String ] message The error message. + # @param [ Operation::Result ] result The result object. + # @param [ Hash ] options Additional parameters. + # + # @option options [ Integer ] :code Error code. + # @option options [ String ] :code_name Error code name. + # @option options [ BSON::Document ] :document The server-returned + # error document. + # @option options [ String ] server_message The server-returned + # error message parsed from the response. + # @option options [ Hash ] :write_concern_error_document The + # server-supplied write concern error document, if any. + # @option options [ Integer ] :write_concern_error_code Error code for + # write concern error, if any. + # @option options [ String ] :write_concern_error_code_name Error code + # name for write concern error, if any. + # @option options [ Array ] :write_concern_error_labels Error + # labels for the write concern error, if any. + # @option options [ Array ] :labels The set of labels associated + # with the error. + # @option options [ true | false ] :wtimeout Whether the error is a wtimeout. + def initialize(message = nil, result = nil, options = {}) + @details = retrieve_details(options[:document]) + super(append_details(message, @details)) - # Whether the error is a write concern timeout. - # - # @return [ true | false ] Whether the error is a write concern timeout. - # - # @since 2.7.1 - def wtimeout? - @wtimeout - end + @result = result + @code = options[:code] + @code_name = options[:code_name] + @write_concern_error_document = options[:write_concern_error_document] + @write_concern_error_code = options[:write_concern_error_code] + @write_concern_error_code_name = options[:write_concern_error_code_name] + @write_concern_error_labels = options[:write_concern_error_labels] || [] + @labels = options[:labels] || [] + @wtimeout = !!options[:wtimeout] + @document = options[:document] + @server_message = options[:server_message] + end - # Whether the error is MaxTimeMSExpired. - # - # @return [ true | false ] Whether the error is MaxTimeMSExpired. - # - # @since 2.10.0 - def max_time_ms_expired? - code == 50 # MaxTimeMSExpired - end + # Whether the error is a write concern timeout. + # + # @return [ true | false ] Whether the error is a write concern timeout. + # + # @since 2.7.1 + def wtimeout? + @wtimeout + end - # Whether the error is caused by an attempted retryable write - # on a storage engine that does not support retryable writes. - # - # @return [ true | false ] Whether the error is caused by an attempted - # retryable write on a storage engine that does not support retryable writes. - # - # @since 2.10.0 - def unsupported_retryable_write? - # code 20 is IllegalOperation. - # Note that the document is expected to be a BSON::Document, thus - # either having string keys or providing indifferent access. - code == 20 && server_message&.start_with?("Transaction numbers") || false - end + # Whether the error is MaxTimeMSExpired. + # + # @return [ true | false ] Whether the error is MaxTimeMSExpired. + # + # @since 2.10.0 + def max_time_ms_expired? + code == 50 # MaxTimeMSExpired + end + + # Whether the error is caused by an attempted retryable write + # on a storage engine that does not support retryable writes. + # + # @return [ true | false ] Whether the error is caused by an attempted + # retryable write on a storage engine that does not support retryable writes. + # + # @since 2.10.0 + def unsupported_retryable_write? + # code 20 is IllegalOperation. + # Note that the document is expected to be a BSON::Document, thus + # either having string keys or providing indifferent access. + code == 20 && server_message&.start_with?("Transaction numbers") || false + end - private + private - # Retrieve the details from a document - # - # @return [ Hash | nil ] the details extracted from the document - def retrieve_details(document) - return nil unless document - if wce = document['writeConcernError'] - return wce['errInfo'] - elsif we = document['writeErrors']&.first - return we['errInfo'] + # Retrieve the details from a document + # + # @return [ Hash | nil ] the details extracted from the document + def retrieve_details(document) + return nil unless document + if wce = document['writeConcernError'] + return wce['errInfo'] + elsif we = document['writeErrors']&.first + return we['errInfo'] + end end - end - # Append the details to the message - # - # @return [ String ] the message with the details appended to it - def append_details(message, details) - return message unless details && message - message + " -- #{details.to_json}" + # Append the details to the message + # + # @return [ String ] the message with the details appended to it + def append_details(message, details) + return message unless details && message + message + " -- #{details.to_json}" + end end + + # OperationFailure is the canonical implementor of the + # OperationFailure::Family concern. + include OperationFailure::Family end end end diff --git a/lib/mongo/error/server_timeout_error.rb b/lib/mongo/error/server_timeout_error.rb new file mode 100644 index 0000000000..d3e66a0eaf --- /dev/null +++ b/lib/mongo/error/server_timeout_error.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'mongo/error/timeout_error' + +module Mongo + class Error + # Raised when the server returns error code 50. + class ServerTimeoutError < TimeoutError + include OperationFailure::Family + end + end +end diff --git a/lib/mongo/error/socket_timeout_error.rb b/lib/mongo/error/socket_timeout_error.rb index 25b5980fd8..b8332f61f1 100644 --- a/lib/mongo/error/socket_timeout_error.rb +++ b/lib/mongo/error/socket_timeout_error.rb @@ -15,13 +15,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'mongo/error/timeout_error' + module Mongo class Error # Raised when a socket connection times out. # # @since 2.0.0 - class SocketTimeoutError < Error + class SocketTimeoutError < TimeoutError include WriteRetryable include ChangeStreamResumable end diff --git a/lib/mongo/error/timeout_error.rb b/lib/mongo/error/timeout_error.rb new file mode 100644 index 0000000000..a607f002dd --- /dev/null +++ b/lib/mongo/error/timeout_error.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# Copyright (C) 2015-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. + +module Mongo + class Error + # Raised when a Client Side Operation Timeout times out. + class TimeoutError < Error + end + end +end diff --git a/lib/mongo/grid/fs_bucket.rb b/lib/mongo/grid/fs_bucket.rb index 8d723cdf55..4fbc5218c1 100644 --- a/lib/mongo/grid/fs_bucket.rb +++ b/lib/mongo/grid/fs_bucket.rb @@ -201,8 +201,8 @@ def prefix # @return [ Result ] The result of the remove. # # @since 2.0.0 - def delete_one(file) - delete(file.id) + def delete_one(file, opts = {}) + delete(file.id, opts) end # Remove a single file, identified by its id from the GridFS. @@ -217,9 +217,14 @@ def delete_one(file) # @raise [ Error::FileNotFound ] If the file is not found. # # @since 2.1.0 - def delete(id) - result = files_collection.find({ :_id => id }, @options).delete_one - chunks_collection.find({ :files_id => id }, @options).delete_many + def delete(id, opts = {}) + timeout_holder = CsotTimeoutHolder.new(operation_timeouts: operation_timeouts(opts)) + result = files_collection + .find({ :_id => id }, @options.merge(timeout_ms: timeout_holder.remaining_timeout_ms)) + .delete_one(timeout_ms: timeout_holder.remaining_timeout_ms) + chunks_collection + .find({ :files_id => id }, @options.merge(timeout_ms: timeout_holder.remaining_timeout_ms)) + .delete_many(timeout_ms: timeout_holder.remaining_timeout_ms) raise Error::FileNotFound.new(id, :id) if result.n == 0 result end @@ -485,9 +490,10 @@ def write_concern end # Drop the collections that implement this bucket. - def drop - files_collection.drop - chunks_collection.drop + def drop(opts = {}) + context = Operation::Context.new(operation_timeouts: operation_timeouts(opts)) + files_collection.drop(timeout_ms: context.remaining_timeout_ms) + chunks_collection.drop(timeout_ms: context.remaining_timeout_ms) end private @@ -512,12 +518,24 @@ def files_name "#{prefix}.#{Grid::File::Info::COLLECTION}" end - def ensure_indexes! - if files_collection.find({}, limit: 1, projection: { _id: 1 }).first.nil? + def ensure_indexes!(timeout_holder = nil) + fc_idx = files_collection.find( + {}, + limit: 1, + projection: { _id: 1 }, + timeout_ms: timeout_holder&.remaining_timeout_ms + ).first + if fc_idx.nil? create_index_if_missing!(files_collection, FSBucket::FILES_INDEX) end - if chunks_collection.find({}, limit: 1, projection: { _id: 1 }).first.nil? + cc_idx = chunks_collection.find( + {}, + limit: 1, + projection: { _id: 1 }, + timeout_ms: timeout_holder&.remaining_timeout_ms + ).first + if cc_idx.nil? create_index_if_missing!(chunks_collection, FSBucket::CHUNKS_INDEX, :unique => true) end end @@ -528,7 +546,7 @@ def create_index_if_missing!(collection, index_spec, options = {}) if indexes_view.get(index_spec).nil? indexes_view.create_one(index_spec, options) end - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e # proceed with index creation if a NamespaceNotFound error is thrown if e.code == 26 indexes_view.create_one(index_spec, options) @@ -537,6 +555,21 @@ def create_index_if_missing!(collection, index_spec, options = {}) end end end + + # @return [ Hash ] timeout_ms value set on the operation level (if any), + # and/or timeout_ms that is set on collection/database/client level (if any). + # + # @api private + def operation_timeouts(opts = {}) + # TODO: We should re-evaluate if we need two timeouts separately. + {}.tap do |result| + if opts[:timeout_ms].nil? + result[:inherited_timeout_ms] = database.timeout_ms + else + result[:operation_timeout_ms] = opts[:timeout_ms] + end + end + end end end end diff --git a/lib/mongo/grid/stream/read.rb b/lib/mongo/grid/stream/read.rb index f33b4a5126..796eaa129b 100644 --- a/lib/mongo/grid/stream/read.rb +++ b/lib/mongo/grid/stream/read.rb @@ -59,6 +59,12 @@ def initialize(fs, options) @file_id = @options.delete(:file_id) @options.freeze @open = true + @timeout_holder = CsotTimeoutHolder.new( + operation_timeouts: { + operation_timeout_ms: options[:timeout_ms], + inherited_timeout_ms: fs.database.timeout_ms + } + ) end # Iterate through chunk data streamed from the FSBucket. @@ -178,7 +184,11 @@ def read_preference # @since 2.1.0 def file_info @file_info ||= begin - doc = options[:file_info_doc] || fs.files_collection.find(_id: file_id).first + doc = options[:file_info_doc] || + fs.files_collection.find( + { _id: file_id }, + { timeout_ms: @timeout_holder.remaining_timeout_ms! } + ).first if doc File::Info.new(Options::Mapper.transform(doc, File::Info::MAPPINGS.invert)) else @@ -209,6 +219,10 @@ def view else options end + if @timeout_holder.csot? + opts[:timeout_ms] = @timeout_holder.remaining_timeout_ms! + opts[:timeout_mode] = :cursor_lifetime + end fs.chunks_collection.find({ :files_id => file_id }, opts).sort(:n => 1) end diff --git a/lib/mongo/grid/stream/write.rb b/lib/mongo/grid/stream/write.rb index 75ef68c56b..4ff2dc0a34 100644 --- a/lib/mongo/grid/stream/write.rb +++ b/lib/mongo/grid/stream/write.rb @@ -83,6 +83,12 @@ def initialize(fs, options) @options.freeze @filename = @options[:filename] @open = true + @timeout_holder = CsotTimeoutHolder.new( + operation_timeouts: { + operation_timeout_ms: options[:timeout_ms], + inherited_timeout_ms: fs.database.timeout_ms + } + ) end # Write to the GridFS bucket from the source stream or a string. @@ -107,7 +113,12 @@ def write(io) end chunks = File::Chunk.split(io, file_info, @n) @n += chunks.size - chunks_collection.insert_many(chunks) unless chunks.empty? + unless chunks.empty? + chunks_collection.insert_many( + chunks, + timeout_ms: @timeout_holder.remaining_timeout_ms! + ) + end self end @@ -124,7 +135,10 @@ def write(io) def close ensure_open! update_length - files_collection.insert_one(file_info, @options) + files_collection.insert_one( + file_info, + @options.merge(timeout_ms: @timeout_holder.remaining_timeout_ms!) + ) @open = false file_id end @@ -166,7 +180,10 @@ def closed? # # @since 2.1.0 def abort - fs.chunks_collection.find({ :files_id => file_id }, @options).delete_many + fs.chunks_collection.find( + { :files_id => file_id }, + @options.merge(timeout_ms: @timeout_holder.remaining_timeout_ms!) + ).delete_many (@open = false) || true end @@ -200,7 +217,7 @@ def file_info end def ensure_indexes! - fs.send(:ensure_indexes!) + fs.send(:ensure_indexes!, @timeout_holder) end def ensure_open! diff --git a/lib/mongo/index/view.rb b/lib/mongo/index/view.rb index 3a45842899..4e8c41b742 100644 --- a/lib/mongo/index/view.rb +++ b/lib/mongo/index/view.rb @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'mongo/cursor/nontailable' + module Mongo module Index @@ -25,6 +27,8 @@ class View extend Forwardable include Enumerable include Retryable + include Mongo::CursorHost + include Cursor::NonTailable # @return [ Collection ] collection The indexes collection. attr_reader :collection @@ -33,6 +37,12 @@ class View # when sending the listIndexes command. attr_reader :batch_size + # @return [ Integer | nil | The timeout_ms value that was passed as an + # option to the view. + # + # @api private + attr_reader :operation_timeout_ms + def_delegators :@collection, :cluster, :database, :read_preference, :write_concern, :client def_delegators :cluster, :next_primary @@ -90,7 +100,7 @@ class View # @since 2.0.0 def drop_one(name, options = {}) raise Error::MultiIndexDrop.new if name == Index::ALL - drop_by_name(name, comment: options[:comment]) + drop_by_name(name, options) end # Drop all indexes on the collection. @@ -107,7 +117,7 @@ def drop_one(name, options = {}) # # @since 2.0.0 def drop_all(options = {}) - drop_by_name(Index::ALL, comment: options[:comment]) + drop_by_name(Index::ALL, options) end # Creates an index on the collection. @@ -161,7 +171,7 @@ def create_one(keys, options = {}) if session = @options[:session] create_options[:session] = session end - %i(commit_quorum session comment).each do |key| + %i(commit_quorum session comment timeout_ms max_time_ms).each do |key| if value = options.delete(key) create_options[key] = value end @@ -210,7 +220,7 @@ def create_many(*models) options = models.pop end - client.send(:with_session, @options.merge(options)) do |session| + client.with_session(@options.merge(options)) do |session| server = next_primary(nil, session) indexes = normalize_models(models, server) @@ -229,8 +239,12 @@ def create_many(*models) write_concern: write_concern, comment: options[:comment], } - - Operation::CreateIndex.new(spec).execute(server, context: Operation::Context.new(client: client, session: session)) + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(options) + ) + Operation::CreateIndex.new(spec).execute(server, context: context) end end @@ -263,9 +277,15 @@ def get(keys_or_name) # # @since 2.0.0 def each(&block) - session = client.send(:get_session, @options) - cursor = read_with_retry_cursor(session, ServerSelector.primary, self) do |server| - send_initial_query(server, session) + session = client.get_session(@options) + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(@options) + ) + + cursor = read_with_retry_cursor(session, ServerSelector.primary, self, context: context) do |server| + send_initial_query(server, session, context) end if block_given? cursor.each do |doc| @@ -283,22 +303,53 @@ def each(&block) # # @param [ Collection ] collection The collection. # @param [ Hash ] options Options for getting a list of indexes. - # Only relevant for when the listIndexes command is used with server - # versions >=2.8. # # @option options [ Integer ] :batch_size The batch size for results # returned from the listIndexes command. + # @option options [ :cursor_lifetime | :iteration ] :timeout_mode How to interpret + # :timeout_ms (whether it applies to the lifetime of the cursor, or per + # iteration). + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the collection or the database or the client. # # @since 2.0.0 def initialize(collection, options = {}) @collection = collection + @operation_timeout_ms = options.delete(:timeout_ms) + + validate_timeout_mode!(options) + @batch_size = options[:batch_size] @options = options end + # The timeout_ms value to use for this operation; either specified as an + # option to the view, or inherited from the collection. + # + # @return [ Integer | nil ] the timeout_ms for this operation + def timeout_ms + operation_timeout_ms || collection.timeout_ms + end + + # @return [ Hash ] timeout_ms value set on the operation level (if any), + # and/or timeout_ms that is set on collection/database/client level (if any). + # + # @api private + def operation_timeouts(opts = {}) + {}.tap do |result| + if opts[:timeout_ms] || operation_timeout_ms + result[:operation_timeout_ms] = opts.delete(:timeout_ms) || operation_timeout_ms + else + result[:inherited_timeout_ms] = collection.timeout_ms + end + end + end + private - def drop_by_name(name, comment: nil) + def drop_by_name(name, opts = {}) client.send(:with_session, @options) do |session| spec = { db_name: database.name, @@ -307,9 +358,14 @@ def drop_by_name(name, comment: nil) session: session, write_concern: write_concern, } - spec[:comment] = comment unless comment.nil? + spec[:comment] = opts[:comment] unless opts[:comment].nil? server = next_primary(nil, session) - Operation::DropIndex.new(spec).execute(server, context: Operation::Context.new(client: client, session: session)) + context = Operation::Context.new( + client: client, + session: session, + operation_timeouts: operation_timeouts(opts) + ) + Operation::DropIndex.new(spec).execute(server, context: context) end end @@ -347,8 +403,13 @@ def normalize_models(models, server) end end - def send_initial_query(server, session) - initial_query_op(session).execute(server, context: Operation::Context.new(client: client, session: session)) + def send_initial_query(server, session, context) + if server.load_balancer? + connection = server.pool.check_out(context: context) + initial_query_op(session).execute_with_connection(connection, context: context) + else + initial_query_op(session).execute(server, context: context) + end end end end diff --git a/lib/mongo/operation.rb b/lib/mongo/operation.rb index 8def25dbe3..c7d7140121 100644 --- a/lib/mongo/operation.rb +++ b/lib/mongo/operation.rb @@ -22,6 +22,7 @@ require 'mongo/operation/shared/validatable' require 'mongo/operation/shared/object_id_generator' require 'mongo/operation/shared/op_msg_executable' +require 'mongo/operation/shared/timed' require 'mongo/operation/op_msg_base' require 'mongo/operation/command' diff --git a/lib/mongo/operation/context.rb b/lib/mongo/operation/context.rb index b7cae91d5a..03d6e0957d 100644 --- a/lib/mongo/operation/context.rb +++ b/lib/mongo/operation/context.rb @@ -34,8 +34,15 @@ module Operation # operations. # # @api private - class Context - def initialize(client: nil, session: nil, connection_global_id: nil, options: nil) + class Context < CsotTimeoutHolder + def initialize( + client: nil, + session: nil, + connection_global_id: nil, + operation_timeouts: {}, + view: nil, + options: nil + ) if options if client raise ArgumentError, 'Client and options cannot both be specified' @@ -52,14 +59,33 @@ def initialize(client: nil, session: nil, connection_global_id: nil, options: ni @client = client @session = session + @view = view @connection_global_id = connection_global_id @options = options + super(session: session, operation_timeouts: operation_timeouts) end attr_reader :client attr_reader :session + attr_reader :view attr_reader :options + # Returns a new Operation::Context with the deadline refreshed + # and relative to the current moment. + # + # @return [ Operation::Context ] the refreshed context + def refresh(connection_global_id: @connection_global_id, timeout_ms: nil, view: nil) + operation_timeouts = @operation_timeouts + operation_timeouts = operation_timeouts.merge(operation_timeout_ms: timeout_ms) if timeout_ms + + self.class.new(client: client, + session: session, + connection_global_id: connection_global_id, + operation_timeouts: operation_timeouts, + view: view || self.view, + options: options) + end + def connection_global_id @connection_global_id || session&.pinned_connection_global_id end @@ -122,10 +148,18 @@ def encrypt? client&.encrypter&.encrypt? || false end + def encrypt(db_name, cmd) + encrypter.encrypt(db_name, cmd, self) + end + def decrypt? !!client&.encrypter end + def decrypt(cmd) + encrypter.decrypt(cmd, self) + end + def encrypter if client&.encrypter client.encrypter @@ -133,6 +167,10 @@ def encrypter raise Error::InternalDriverError, 'Encrypter should only be accessed when encryption is to be performed' end end + + def inspect + "#<#{self.class} connection_global_id=#{connection_global_id.inspect} deadline=#{deadline.inspect} options=#{options.inspect} operation_timeouts=#{operation_timeouts.inspect}>" + end end end end diff --git a/lib/mongo/operation/create_search_indexes/op_msg.rb b/lib/mongo/operation/create_search_indexes/op_msg.rb index 444d35721b..a036d1e394 100644 --- a/lib/mongo/operation/create_search_indexes/op_msg.rb +++ b/lib/mongo/operation/create_search_indexes/op_msg.rb @@ -14,11 +14,11 @@ class OpMsg < OpMsgBase # Returns the command to send to the database, describing the # desired createSearchIndexes operation. # - # @param [ Mongo::Server ] _server the server that will receive the + # @param [ Connection ] _connection the connection that will receive the # command # # @return [ Hash ] the selector - def selector(_server) + def selector(_connection) { createSearchIndexes: coll_name, :$db => db_name, diff --git a/lib/mongo/operation/delete/op_msg.rb b/lib/mongo/operation/delete/op_msg.rb index f1435f9564..4ee081478d 100644 --- a/lib/mongo/operation/delete/op_msg.rb +++ b/lib/mongo/operation/delete/op_msg.rb @@ -49,7 +49,8 @@ def selector(connection) def message(connection) section = Protocol::Msg::Section1.new(IDENTIFIER, send(IDENTIFIER)) - Protocol::Msg.new(flags, {}, command(connection), section) + cmd = apply_relevant_timeouts_to(command(connection), connection) + Protocol::Msg.new(flags, {}, cmd, section) end end end diff --git a/lib/mongo/operation/drop_search_index/op_msg.rb b/lib/mongo/operation/drop_search_index/op_msg.rb index 8f4d323c55..1a27b6d0ba 100644 --- a/lib/mongo/operation/drop_search_index/op_msg.rb +++ b/lib/mongo/operation/drop_search_index/op_msg.rb @@ -14,11 +14,11 @@ class OpMsg < OpMsgBase # Returns the command to send to the database, describing the # desired dropSearchIndex operation. # - # @param [ Mongo::Server ] _server the server that will receive the + # @param [ Connection ] _connection the connection that will receive the # command # # @return [ Hash ] the selector - def selector(_server) + def selector(_connection) { dropSearchIndex: coll_name, :$db => db_name, diff --git a/lib/mongo/operation/find/op_msg.rb b/lib/mongo/operation/find/op_msg.rb index f29ec4686b..28b8908ba3 100644 --- a/lib/mongo/operation/find/op_msg.rb +++ b/lib/mongo/operation/find/op_msg.rb @@ -31,6 +31,51 @@ class OpMsg < OpMsgBase private + # Applies the relevant CSOT timeouts for a find command. + # Considers the cursor type and timeout mode and will add (or omit) a + # maxTimeMS field accordingly. + def apply_relevant_timeouts_to(spec, connection) + with_max_time(connection) do |max_time_sec| + timeout_ms = max_time_sec ? (max_time_sec * 1_000).to_i : nil + apply_find_timeouts_to(spec, timeout_ms) unless connection.description.mongocryptd? + end + end + + def apply_find_timeouts_to(spec, timeout_ms) + view = context&.view + return spec unless view + + case view.cursor_type + when nil # non-tailable + if view.timeout_mode == :cursor_lifetime + spec[:maxTimeMS] = timeout_ms || view.options[:max_time_ms] + else # timeout_mode == :iterable + # drivers MUST honor the timeoutMS option for the initial command + # but MUST NOT append a maxTimeMS field to the command sent to the + # server + if !timeout_ms && view.options[:max_time_ms] + spec[:maxTimeMS] = view.options[:max_time_ms] + end + end + + when :tailable + # If timeoutMS is set, drivers...MUST NOT append a maxTimeMS field to any commands. + if !timeout_ms && view.options[:max_time_ms] + spec[:maxTimeMS] = view.options[:max_time_ms] + end + + when :tailable_await + # The server supports the maxTimeMS option for the original command. + if timeout_ms || view.options[:max_time_ms] + spec[:maxTimeMS] = timeout_ms || view.options[:max_time_ms] + end + end + + spec.tap do |spc| + spc.delete(:maxTimeMS) if spc[:maxTimeMS].nil? + end + end + def selector(connection) # The mappings are BSON::Documents and as such store keys as # strings, the spec here has symbol keys. diff --git a/lib/mongo/operation/get_more/op_msg.rb b/lib/mongo/operation/get_more/op_msg.rb index cad0ab3bbe..777717b25b 100644 --- a/lib/mongo/operation/get_more/op_msg.rb +++ b/lib/mongo/operation/get_more/op_msg.rb @@ -28,6 +28,39 @@ class OpMsg < OpMsgBase include ExecutableTransactionLabel include PolymorphicResult include CommandBuilder + + private + + # Applies the relevant CSOT timeouts for a getMore command. + # Considers the cursor type and timeout mode and will add (or omit) a + # maxTimeMS field accordingly. + def apply_relevant_timeouts_to(spec, connection) + with_max_time(connection) do |max_time_sec| + timeout_ms = max_time_sec ? (max_time_sec * 1_000).to_i : nil + apply_get_more_timeouts_to(spec, timeout_ms) + end + end + + def apply_get_more_timeouts_to(spec, timeout_ms) + view = context&.view + return spec unless view + + if view.cursor_type == :tailable_await + # If timeoutMS is set, drivers MUST apply it to the original operation. + # Drivers MUST also apply the original timeoutMS value to each next + # call on the resulting cursor but MUST NOT use it to derive a + # maxTimeMS value for getMore commands. Helpers for operations that + # create tailable awaitData cursors MUST also support the + # maxAwaitTimeMS option. Drivers MUST error if this option is set, + # timeoutMS is set to a non-zero value, and maxAwaitTimeMS is greater + # than or equal to timeoutMS. If this option is set, drivers MUST use + # it as the maxTimeMS field on getMore commands. + max_await_time_ms = view.respond_to?(:max_await_time_ms) ? view.max_await_time_ms : nil + spec[:maxTimeMS] = max_await_time_ms if max_await_time_ms + end + + spec + end end end end diff --git a/lib/mongo/operation/insert/op_msg.rb b/lib/mongo/operation/insert/op_msg.rb index 7ab863e6af..39b299ef76 100644 --- a/lib/mongo/operation/insert/op_msg.rb +++ b/lib/mongo/operation/insert/op_msg.rb @@ -35,7 +35,7 @@ class OpMsg < OpMsgBase def get_result(connection, context, options = {}) # This is a Mongo::Operation::Insert::Result - Result.new(*dispatch_message(connection, context), @ids) + Result.new(*dispatch_message(connection, context), @ids, context: context) end def selector(connection) @@ -49,7 +49,8 @@ def selector(connection) def message(connection) section = Protocol::Msg::Section1.new(IDENTIFIER, send(IDENTIFIER)) - Protocol::Msg.new(flags, {}, command(connection), section) + cmd = apply_relevant_timeouts_to(command(connection), connection) + Protocol::Msg.new(flags, {}, cmd, section) end end end diff --git a/lib/mongo/operation/insert/result.rb b/lib/mongo/operation/insert/result.rb index 05995de079..cdde68e252 100644 --- a/lib/mongo/operation/insert/result.rb +++ b/lib/mongo/operation/insert/result.rb @@ -47,11 +47,13 @@ class Result < Operation::Result # Global id of the connection on which the operation that # this result is for was performed. # @param [ Array ] ids The ids of the inserted documents. + # @param [ Operation::Context | nil ] context the operation context that + # was active when this result was produced. # # @since 2.0.0 # @api private - def initialize(replies, connection_description, connection_global_id, ids) - super(replies, connection_description, connection_global_id) + def initialize(replies, connection_description, connection_global_id, ids, context: nil) + super(replies, connection_description, connection_global_id, context: context) @inserted_ids = ids end diff --git a/lib/mongo/operation/list_collections/result.rb b/lib/mongo/operation/list_collections/result.rb index 9b45e8b4fb..e964882f04 100644 --- a/lib/mongo/operation/list_collections/result.rb +++ b/lib/mongo/operation/list_collections/result.rb @@ -85,7 +85,7 @@ def validate! if successful? self else - raise Error::OperationFailure.new( + raise operation_failure_class.new( parser.message, self, code: parser.code, diff --git a/lib/mongo/operation/map_reduce/result.rb b/lib/mongo/operation/map_reduce/result.rb index 8959a99d15..6e6660ee93 100644 --- a/lib/mongo/operation/map_reduce/result.rb +++ b/lib/mongo/operation/map_reduce/result.rb @@ -108,7 +108,7 @@ def time # @example Validate the result. # result.validate! # - # @raise [ Error::OperationFailure ] If an error is in the result. + # @raise [ Error::OperationFailure::Family ] If an error is in the result. # # @return [ Result ] The result if verification passed. # diff --git a/lib/mongo/operation/op_msg_base.rb b/lib/mongo/operation/op_msg_base.rb index 5f00d42aec..5716227cd1 100644 --- a/lib/mongo/operation/op_msg_base.rb +++ b/lib/mongo/operation/op_msg_base.rb @@ -22,11 +22,13 @@ class OpMsgBase include Specifiable include Executable include SessionsSupported + include Timed private def message(connection) - Protocol::Msg.new(flags, options(connection), command(connection)) + cmd = apply_relevant_timeouts_to(command(connection), connection) + Protocol::Msg.new(flags, options(connection), cmd) end end end diff --git a/lib/mongo/operation/result.rb b/lib/mongo/operation/result.rb index d6989444b8..42de5d5720 100644 --- a/lib/mongo/operation/result.rb +++ b/lib/mongo/operation/result.rb @@ -100,9 +100,13 @@ class Result # @param [ Integer ] connection_global_id # Global id of the connection on which the operation that # this result is for was performed. + # @param [ Operation::Context | nil ] context the context that was active + # when this result was produced. # # @api private - def initialize(replies, connection_description = nil, connection_global_id = nil) + def initialize(replies, connection_description = nil, connection_global_id = nil, context: nil, connection: nil) + @context = context + if replies if replies.is_a?(Array) if replies.length != 1 @@ -118,6 +122,7 @@ def initialize(replies, connection_description = nil, connection_global_id = nil @replies = [ reply ] @connection_description = connection_description @connection_global_id = connection_global_id + @connection = connection end end @@ -138,6 +143,14 @@ def initialize(replies, connection_description = nil, connection_global_id = nil # @api private attr_reader :connection_global_id + # @return [ Operation::Context | nil ] the operation context (if any) + # that was active when this result was produced. + # + # @api private + attr_reader :context + + attr_reader :connection + # @api private def_delegators :parser, :not_master?, :node_recovering?, :node_shutting_down? @@ -320,7 +333,7 @@ def ok? # @example Validate the result. # result.validate! # - # @raise [ Error::OperationFailure ] If an error is in the result. + # @raise [ Error::OperationFailure::Family ] If an error is in the result. # # @return [ Result ] The result if verification passed. # @@ -330,16 +343,16 @@ def validate! !successful? ? raise_operation_failure : self end - # The exception instance (of the Error::OperationFailure class) + # The exception instance (of Error::OperationFailure::Family) # that would be raised during processing of this result. # # This method should only be called when result is not successful. # - # @return [ Error::OperationFailure ] The exception. + # @return [ Error::OperationFailure::Family ] The exception. # # @api private def error - @error ||= Error::OperationFailure.new( + @error ||= operation_failure_class.new( parser.message, self, code: parser.code, @@ -453,6 +466,14 @@ def snapshot_timestamp private + def operation_failure_class + if context&.csot? && parser.code == 50 + Error::ServerTimeoutError + else + Error::OperationFailure + end + end + def aggregate_returned_count replies.reduce(0) do |n, reply| n += reply.number_returned diff --git a/lib/mongo/operation/shared/executable.rb b/lib/mongo/operation/shared/executable.rb index 9b61476631..041e4d1e5b 100644 --- a/lib/mongo/operation/shared/executable.rb +++ b/lib/mongo/operation/shared/executable.rb @@ -28,7 +28,18 @@ module Executable include ResponseHandling + # @return [ Operation::Context | nil ] the operation context used to + # execute this operation. + attr_accessor :context + def do_execute(connection, context, options = {}) + # Save the context on the instance, to avoid having to pass it as a + # parameter to every single method. There are many legacy methods that + # still accept it as a parameter, which are left as-is for now to + # minimize the impact of this change. Moving forward, it may be + # reasonable to refactor things so this saved reference is used instead. + @context = context + session&.materialize_if_needed unpin_maybe(session, connection) do add_error_labels(connection, context) do @@ -93,7 +104,7 @@ def result_class end def get_result(connection, context, options = {}) - result_class.new(*dispatch_message(connection, context, options)) + result_class.new(*dispatch_message(connection, context, options), context: context, connection: connection) end # Returns a Protocol::Message or nil as reply. diff --git a/lib/mongo/operation/shared/op_msg_executable.rb b/lib/mongo/operation/shared/op_msg_executable.rb index a97b8fd48d..99d890de9b 100644 --- a/lib/mongo/operation/shared/op_msg_executable.rb +++ b/lib/mongo/operation/shared/op_msg_executable.rb @@ -32,7 +32,10 @@ module OpMsgExecutable # # @return [ Mongo::Operation::Result ] The operation result. def execute(server, context:, options: {}) - server.with_connection(connection_global_id: context.connection_global_id) do |connection| + server.with_connection( + connection_global_id: context.connection_global_id, + context: context + ) do |connection| execute_with_connection(connection, context: context, options: options) end end diff --git a/lib/mongo/operation/shared/response_handling.rb b/lib/mongo/operation/shared/response_handling.rb index 799721a8de..36f4f8dafd 100644 --- a/lib/mongo/operation/shared/response_handling.rb +++ b/lib/mongo/operation/shared/response_handling.rb @@ -42,7 +42,7 @@ def validate_result(result, connection, context) # Adds error labels to exceptions raised in the yielded to block, # which should perform MongoDB operations and raise Mongo::Errors on # failure. This method handles network errors (Error::SocketError) - # and server-side errors (Error::OperationFailure); it does not + # and server-side errors (Error::OperationFailure::Family); it does not # handle server selection errors (Error::NoServerAvailable), for which # labels are added in the server selection code. # @@ -65,7 +65,7 @@ def add_error_labels(connection, context) rescue Mongo::Error::SocketTimeoutError => e maybe_add_retryable_write_error_label!(e, connection, context) raise e - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e if context.committing_transaction? if e.write_retryable? || e.wtimeout? || (e.write_concern_error? && !Session::UNLABELED_WRITE_CONCERN_CODES.include?(e.write_concern_error_code) @@ -104,7 +104,7 @@ def unpin_maybe(session, connection) # raised during execution of operations on servers. def add_server_diagnostics(connection) yield - rescue Error::SocketError, Error::SocketTimeoutError + rescue Error::SocketError, Error::SocketTimeoutError, Error::TimeoutError # Diagnostics should have already been added by the connection code, # do not add them again. raise diff --git a/lib/mongo/operation/shared/sessions_supported.rb b/lib/mongo/operation/shared/sessions_supported.rb index aed9bd23e6..0eb9d216b9 100644 --- a/lib/mongo/operation/shared/sessions_supported.rb +++ b/lib/mongo/operation/shared/sessions_supported.rb @@ -114,7 +114,7 @@ def apply_read_pref!(selector) end def apply_txn_opts!(selector) - session.add_txn_opts!(selector, read_command?(selector)) + session.add_txn_opts!(selector, read_command?(selector), context) end def suppress_read_write_concern!(selector) diff --git a/lib/mongo/operation/shared/timed.rb b/lib/mongo/operation/shared/timed.rb new file mode 100644 index 0000000000..a023e0a3a8 --- /dev/null +++ b/lib/mongo/operation/shared/timed.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Mongo + module Operation + # Defines the behavior of operations that have the default timeout + # behavior described by the client-side operation timeouts (CSOT) + # spec. + # + # @api private + module Timed + # If a timeout is active (as defined by the current context), and it has + # not yet expired, add :maxTimeMS to the spec. + # + # @param [ Hash ] spec The spec to modify + # @param [ Connection ] connection The connection that will be used to + # execute the operation + # + # @return [ Hash ] the spec + # + # @raises [ Mongo::Error::TimeoutError ] if the current timeout has + # expired. + def apply_relevant_timeouts_to(spec, connection) + with_max_time(connection) do |max_time_sec| + return spec if max_time_sec.nil? + return spec if connection.description.mongocryptd? + + spec.tap { spec[:maxTimeMS] = (max_time_sec * 1_000).to_i } + end + end + + # A helper method that computes the remaining timeout (in seconds) and + # yields it to the associated block. If no timeout is present, yields + # nil. If the timeout has expired, raises Mongo::Error::TimeoutError. + # + # @param [ Connection ] connection The connection that will be used to + # execute the operation + # + # @return [ Hash ] the result of yielding to the block (which must be + # a Hash) + def with_max_time(connection) + if context&.timeout? + max_time_sec = context.remaining_timeout_sec - connection.server.minimum_round_trip_time + raise Mongo::Error::TimeoutError if max_time_sec <= 0 + + yield max_time_sec + else + yield nil + end + end + end + end +end diff --git a/lib/mongo/operation/shared/write.rb b/lib/mongo/operation/shared/write.rb index 68214fac36..2f9f50cd47 100644 --- a/lib/mongo/operation/shared/write.rb +++ b/lib/mongo/operation/shared/write.rb @@ -35,7 +35,10 @@ module Write # # @since 2.5.2 def execute(server, context:) - server.with_connection(connection_global_id: context.connection_global_id) do |connection| + server.with_connection( + connection_global_id: context.connection_global_id, + context: context + ) do |connection| execute_with_connection(connection, context: context) end end diff --git a/lib/mongo/operation/update/op_msg.rb b/lib/mongo/operation/update/op_msg.rb index 23ac1f4b4b..9606cd7d86 100644 --- a/lib/mongo/operation/update/op_msg.rb +++ b/lib/mongo/operation/update/op_msg.rb @@ -45,7 +45,8 @@ def selector(connection) def message(connection) updates = validate_updates(connection, send(IDENTIFIER)) section = Protocol::Msg::Section1.new(IDENTIFIER, updates) - Protocol::Msg.new(flags, {}, command(connection), section) + cmd = apply_relevant_timeouts_to(command(connection), connection) + Protocol::Msg.new(flags, {}, cmd, section) end end end diff --git a/lib/mongo/operation/update_search_index/op_msg.rb b/lib/mongo/operation/update_search_index/op_msg.rb index c6d21aaf0d..2030b30735 100644 --- a/lib/mongo/operation/update_search_index/op_msg.rb +++ b/lib/mongo/operation/update_search_index/op_msg.rb @@ -14,11 +14,11 @@ class OpMsg < OpMsgBase # Returns the command to send to the database, describing the # desired updateSearchIndex operation. # - # @param [ Mongo::Server ] _server the server that will receive the + # @param [ Connection ] _connection the connection that will receive the # command # # @return [ Hash ] the selector - def selector(_server) + def selector(_connection) { updateSearchIndex: coll_name, :$db => db_name, diff --git a/lib/mongo/protocol/message.rb b/lib/mongo/protocol/message.rb index e0baf5ed5a..2ea6330735 100644 --- a/lib/mongo/protocol/message.rb +++ b/lib/mongo/protocol/message.rb @@ -244,10 +244,7 @@ def self.deserialize(io, # timeout option. For compatibility with whoever might call this # method with some other IO-like object, pass options only when they # are not empty. - read_options = {} - if timeout = options[:socket_timeout] - read_options[:timeout] = timeout - end + read_options = options.slice(:timeout, :socket_timeout) if read_options.empty? chunk = io.read(16) diff --git a/lib/mongo/protocol/msg.rb b/lib/mongo/protocol/msg.rb index 73df7a31b6..46aa7092e6 100644 --- a/lib/mongo/protocol/msg.rb +++ b/lib/mongo/protocol/msg.rb @@ -226,7 +226,7 @@ def maybe_encrypt(connection, context) db_name = @main_document[DATABASE_IDENTIFIER] cmd = merge_sections - enc_cmd = context.encrypter.encrypt(db_name, cmd) + enc_cmd = context.encrypt(db_name, cmd) if cmd.key?('$db') && !enc_cmd.key?('$db') enc_cmd['$db'] = cmd['$db'] end @@ -251,7 +251,7 @@ def maybe_encrypt(connection, context) def maybe_decrypt(context) if context.decrypt? cmd = merge_sections - enc_cmd = context.encrypter.decrypt(cmd) + enc_cmd = context.decrypt(cmd) Msg.new(@flags, @options, enc_cmd) else self diff --git a/lib/mongo/retryable.rb b/lib/mongo/retryable.rb index 2508e13efd..f93fd22daa 100644 --- a/lib/mongo/retryable.rb +++ b/lib/mongo/retryable.rb @@ -46,8 +46,14 @@ module Retryable # @api private # # @return [ Mongo::Server ] A server matching the server preference. - def select_server(cluster, server_selector, session, failed_server = nil) - server_selector.select_server(cluster, nil, session, deprioritized: [failed_server].compact) + def select_server(cluster, server_selector, session, failed_server = nil, timeout: nil) + server_selector.select_server( + cluster, + nil, + session, + deprioritized: [failed_server].compact, + timeout: timeout + ) end # Returns the read worker for handling retryable reads. diff --git a/lib/mongo/retryable/read_worker.rb b/lib/mongo/retryable/read_worker.rb index d8386f833a..eb3272ca34 100644 --- a/lib/mongo/retryable/read_worker.rb +++ b/lib/mongo/retryable/read_worker.rb @@ -60,19 +60,21 @@ class ReadWorker < BaseWorker # @param [ Mongo::ServerSelector::Selectable ] server_selector Server # selector for the operation. # @param [ CollectionView ] view The +CollectionView+ defining the query. + # @param [ Operation::Context | nil ] context the operation context to use + # with the cursor. # @param [ Proc ] block The block to execute. # # @return [ Cursor ] The cursor for the result set. - def read_with_retry_cursor(session, server_selector, view, &block) - read_with_retry(session, server_selector) do |server| + def read_with_retry_cursor(session, server_selector, view, context: nil, &block) + read_with_retry(session, server_selector, context) do |server| result = yield server # RUBY-2367: This will be updated to allow the query cache to # cache cursors with multi-batch results. if QueryCache.enabled? && !view.collection.system_collection? - CachingCursor.new(view, result, server, session: session) + CachingCursor.new(view, result, server, session: session, context: context) else - Cursor.new(view, result, server, session: session) + Cursor.new(view, result, server, session: session, context: context) end end end @@ -107,16 +109,18 @@ def read_with_retry_cursor(session, server_selector, view, &block) # is being run on. # @param [ Mongo::ServerSelector::Selectable | nil ] server_selector # Server selector for the operation. + # @param [ Mongo::Operation::Context | nil ] context Context for the + # read operation. # @param [ Proc ] block The block to execute. # # @return [ Result ] The result of the operation. - def read_with_retry(session = nil, server_selector = nil, &block) + def read_with_retry(session = nil, server_selector = nil, context = nil, &block) if session.nil? && server_selector.nil? deprecated_legacy_read_with_retry(&block) elsif session&.retry_reads? - modern_read_with_retry(session, server_selector, &block) + modern_read_with_retry(session, server_selector, context, &block) elsif client.max_read_retries > 0 - legacy_read_with_retry(session, server_selector, &block) + legacy_read_with_retry(session, server_selector, context, &block) else read_without_retry(session, server_selector, &block) end @@ -186,17 +190,24 @@ def deprecated_legacy_read_with_retry(&block) # being run on. # @param [ Mongo::ServerSelector::Selectable ] server_selector Server # selector for the operation. + # @param [ Mongo::Operation::Context ] context Context for the + # read operation. # @param [ Proc ] block The block to execute. # # @return [ Result ] The result of the operation. - def modern_read_with_retry(session, server_selector, &block) - server = select_server(cluster, server_selector, session) + def modern_read_with_retry(session, server_selector, context, &block) + server = select_server( + cluster, + server_selector, + session, + timeout: context&.remaining_timeout_sec + ) yield server - rescue *retryable_exceptions, Error::OperationFailure, Auth::Unauthorized, Error::PoolError => e + rescue *retryable_exceptions, Error::OperationFailure::Family, Auth::Unauthorized, Error::PoolError => e e.add_notes('modern retry', 'attempt 1') raise e if session.in_transaction? raise e if !is_retryable_exception?(e) && !e.write_retryable? - retry_read(e, session, server_selector, failed_server: server, &block) + retry_read(e, session, server_selector, context: context, failed_server: server, &block) end # Attempts to do a "legacy" read with retry. The operation will be @@ -207,17 +218,19 @@ def modern_read_with_retry(session, server_selector, &block) # being run on. # @param [ Mongo::ServerSelector::Selectable ] server_selector Server # selector for the operation. + # @param [ Mongo::Operation::Context | nil ] context Context for the + # read operation. # @param [ Proc ] block The block to execute. # # @return [ Result ] The result of the operation. - def legacy_read_with_retry(session, server_selector, &block) + def legacy_read_with_retry(session, server_selector, context = nil, &block) + context&.check_timeout! attempt = attempt ? attempt + 1 : 1 yield select_server(cluster, server_selector, session) - rescue *legacy_retryable_exceptions, Error::OperationFailure => e + rescue *legacy_retryable_exceptions, Error::OperationFailure::Family => e e.add_notes('legacy retry', "attempt #{attempt}") if is_legacy_retryable_exception?(e) - raise e if attempt > client.max_read_retries || session&.in_transaction? elsif e.retryable? && !session&.in_transaction? raise e if attempt > client.max_read_retries @@ -245,7 +258,7 @@ def read_without_retry(session, server_selector, &block) begin yield server - rescue *retryable_exceptions, Error::PoolError, Error::OperationFailure => e + rescue *retryable_exceptions, Error::PoolError, Error::OperationFailure::Family => e e.add_note('retries disabled') raise e end @@ -259,40 +272,67 @@ def read_without_retry(session, server_selector, &block) # being run on. # @param [ Mongo::ServerSelector::Selectable ] server_selector Server # selector for the operation. - # @param [ Mongo::Server ] failed_server The server on which the original + # @param [ Mongo::Operation::Context | nil ] :context Context for the + # read operation. + # @param [ Mongo::Server | nil ] :failed_server The server on which the original # operation failed. # @param [ Proc ] block The block to execute. # # @return [ Result ] The result of the operation. - def retry_read(original_error, session, server_selector, failed_server: nil, &block) - begin - server = select_server(cluster, server_selector, session, failed_server) - rescue Error, Error::AuthError => e - original_error.add_note("later retry failed: #{e.class}: #{e}") - raise original_error - end + def retry_read(original_error, session, server_selector, context: nil, failed_server: nil, &block) + server = select_server_for_retry( + original_error, session, server_selector, context, failed_server + ) log_retry(original_error, message: 'Read retry') begin + context&.check_timeout! + attempt = attempt ? attempt + 1 : 2 yield server, true + rescue Error::TimeoutError + raise rescue *retryable_exceptions => e - e.add_notes('modern retry', 'attempt 2') - raise e - rescue Error::OperationFailure, Error::PoolError => e + e.add_notes('modern retry', "attempt #{attempt}") + if context&.csot? + failed_server = server + retry + else + raise e + end + rescue Error::OperationFailure::Family, Error::PoolError => e e.add_note('modern retry') - unless e.write_retryable? + if e.write_retryable? + e.add_note("attempt #{attempt}") + if context&.csot? + failed_server = server + retry + else + raise e + end + else original_error.add_note("later retry failed: #{e.class}: #{e}") raise original_error end - e.add_note("attempt 2") - raise e rescue Error, Error::AuthError => e e.add_note('modern retry') original_error.add_note("later retry failed: #{e.class}: #{e}") raise original_error end end + + def select_server_for_retry(original_error, session, server_selector, context, failed_server) + select_server( + cluster, + server_selector, + session, + failed_server, + timeout: context&.remaining_timeout_sec + ) + rescue Error, Error::AuthError => e + original_error.add_note("later retry failed: #{e.class}: #{e}") + raise original_error + end end end end diff --git a/lib/mongo/retryable/write_worker.rb b/lib/mongo/retryable/write_worker.rb index 166aceedff..f40d3ae9ca 100644 --- a/lib/mongo/retryable/write_worker.rb +++ b/lib/mongo/retryable/write_worker.rb @@ -74,7 +74,11 @@ def write_with_retry(write_concern, ending_transaction: false, context:, &block) # If we are here, session is not nil. A session being nil would have # failed retry_write_allowed? check. - server = select_server(cluster, ServerSelector.primary, session) + server = select_server( + cluster, ServerSelector.primary, + session, + timeout: context.remaining_timeout_sec + ) unless ending_transaction || server.retry_writes? return legacy_write_with_retry(server, context: context, &block) @@ -110,7 +114,7 @@ def nro_write_with_retry(write_concern, context:, &block) server.with_connection(connection_global_id: context.connection_global_id) do |connection| yield connection, nil, context end - rescue *retryable_exceptions, Error::PoolError, Error::OperationFailure => e + rescue *retryable_exceptions, Error::PoolError, Error::OperationFailure::Family => e e.add_note('retries disabled') raise e end @@ -170,6 +174,7 @@ def ensure_valid_state!(ending_transaction, session) # @api private def legacy_write_with_retry(server = nil, context:) session = context.session + context.check_timeout! # This is the pre-session retry logic, and is not subject to # current retryable write specifications. @@ -177,12 +182,20 @@ def legacy_write_with_retry(server = nil, context:) attempt = 0 begin attempt += 1 - server ||= select_server(cluster, ServerSelector.primary, session) - server.with_connection(connection_global_id: context.connection_global_id) do |connection| + server ||= select_server( + cluster, + ServerSelector.primary, + session, + timeout: context.remaining_timeout_sec + ) + server.with_connection( + connection_global_id: context.connection_global_id, + context: context + ) do |connection| # Legacy retries do not use txn_num yield connection, nil, context.dup end - rescue Error::OperationFailure => e + rescue Error::OperationFailure::Family => e e.add_note('legacy retry') e.add_note("attempt #{attempt}") server = nil @@ -220,7 +233,10 @@ def modern_write_with_retry(session, server, context, &block) txn_num = nil connection_succeeded = false - server.with_connection(connection_global_id: context.connection_global_id) do |connection| + server.with_connection( + connection_global_id: context.connection_global_id, + context: context + ) do |connection| connection_succeeded = true session.materialize_if_needed @@ -230,10 +246,10 @@ def modern_write_with_retry(session, server, context, &block) # it later for the retry as well. yield connection, txn_num, context.dup end - rescue *retryable_exceptions, Error::PoolError, Auth::Unauthorized, Error::OperationFailure => e + rescue *retryable_exceptions, Error::PoolError, Auth::Unauthorized, Error::OperationFailure::Family => e e.add_notes('modern retry', 'attempt 1') - if e.is_a?(Error::OperationFailure) + if e.is_a?(Error::OperationFailure::Family) ensure_retryable!(e) else ensure_labeled_retryable!(e, connection_succeeded, session) @@ -256,6 +272,8 @@ def modern_write_with_retry(session, server, context, &block) # # @return [ Result ] The result of the operation. def retry_write(original_error, txn_num, context:, failed_server: nil, &block) + context&.check_timeout! + session = context.session # We do not request a scan of the cluster here, because error handling @@ -263,7 +281,13 @@ def retry_write(original_error, txn_num, context:, failed_server: nil, &block) # server description and/or topology as necessary (specifically, # a socket error or a not master error should have marked the respective # server unknown). Here we just need to wait for server selection. - server = select_server(cluster, ServerSelector.primary, session, failed_server) + server = select_server( + cluster, + ServerSelector.primary, + session, + failed_server, + timeout: context.remaining_timeout_sec + ) unless server.retry_writes? # Do not need to add "modern retry" here, it should already be on @@ -279,14 +303,21 @@ def retry_write(original_error, txn_num, context:, failed_server: nil, &block) raise Error::RaiseOriginalError end + attempt = attempt ? attempt + 1 : 2 log_retry(original_error, message: 'Write retry') server.with_connection(connection_global_id: context.connection_global_id) do |connection| yield(connection, txn_num, context) end rescue *retryable_exceptions, Error::PoolError => e - fail_on_retryable!(e, original_error) - rescue Error::OperationFailure => e - fail_on_operation_failure!(e, original_error) + maybe_fail_on_retryable(e, original_error, context, attempt) + failed_server = server + retry + rescue Error::OperationFailure::Family => e + maybe_fail_on_operation_failure(e, original_error, context, attempt) + failed_server = server + retry + rescue Mongo::Error::TimeoutError + raise rescue Error, Error::AuthError => e fail_on_other_error!(e, original_error) rescue Error::RaiseOriginalError @@ -332,10 +363,10 @@ def ensure_retryable!(e) # Raise either e, or original_error, depending on whether e is # write_retryable. - def fail_on_retryable!(e, original_error) + def maybe_fail_on_retryable(e, original_error, context, attempt) if e.write_retryable? - e.add_notes('modern retry', 'attempt 2') - raise e + e.add_notes('modern retry', "attempt #{attempt}") + raise e unless context&.deadline else original_error.add_note("later retry failed: #{e.class}: #{e}") raise original_error @@ -344,11 +375,11 @@ def fail_on_retryable!(e, original_error) # Raise either e, or original_error, depending on whether e is # appropriately labeled. - def fail_on_operation_failure!(e, original_error) + def maybe_fail_on_operation_failure(e, original_error, context, attempt) e.add_note('modern retry') if e.label?('RetryableWriteError') && !e.label?('NoWritesPerformed') - e.add_note('attempt 2') - raise e + e.add_note("attempt #{attempt}") + raise e unless context&.deadline else original_error.add_note("later retry failed: #{e.class}: #{e}") raise original_error diff --git a/lib/mongo/server.rb b/lib/mongo/server.rb index d87e70a4f5..c00285034e 100644 --- a/lib/mongo/server.rb +++ b/lib/mongo/server.rb @@ -80,7 +80,7 @@ def initialize(address, cluster, monitoring, event_listeners, options = {}) include Id end @scan_semaphore = DistinguishingSemaphore.new - @round_trip_time_averager = RoundTripTimeAverager.new + @round_trip_time_calculator = RoundTripTimeCalculator.new @description = Description.new(address, {}, load_balancer: !!@options[:load_balancer], force_load_balancer: force_load_balancer?, @@ -197,6 +197,7 @@ def compressor :max_message_size, :tags, :average_round_trip_time, + :minimum_round_trip_time, :mongos?, :other?, :primary?, @@ -228,9 +229,9 @@ def compressor # @api private attr_reader :scan_semaphore - # @return [ RoundTripTimeAverager ] Round trip time averager object. + # @return [ RoundTripTimeCalculator ] Round trip time calculator object. # @api private - attr_reader :round_trip_time_averager + attr_reader :round_trip_time_calculator # Is this server equal to another? # @@ -490,8 +491,12 @@ def reconnect! # @return [ Object ] The result of the block execution. # # @since 2.3.0 - def with_connection(connection_global_id: nil, &block) - pool.with_connection(connection_global_id: connection_global_id, &block) + def with_connection(connection_global_id: nil, context: nil, &block) + pool.with_connection( + connection_global_id: connection_global_id, + context: context, + &block + ) end # Handle handshake failure. @@ -697,5 +702,5 @@ def update_last_scan require 'mongo/server/connection_pool' require 'mongo/server/description' require 'mongo/server/monitor' -require 'mongo/server/round_trip_time_averager' +require 'mongo/server/round_trip_time_calculator' require 'mongo/server/push_monitor' diff --git a/lib/mongo/server/connection.rb b/lib/mongo/server/connection.rb index 349948b61d..f9874764cf 100644 --- a/lib/mongo/server/connection.rb +++ b/lib/mongo/server/connection.rb @@ -226,11 +226,11 @@ def unpin # @return [ true ] If the connection succeeded. # # @since 2.0.0 - def connect! + def connect!(context = nil) raise_if_closed! unless @socket - @socket = create_socket + @socket = create_socket(context) @description, @compressor = do_connect if server.load_balancer? @@ -256,10 +256,16 @@ def connect! # # # @return [ Socket ] The created socket. - private def create_socket + private def create_socket(context = nil) add_server_diagnostics do - address.socket(socket_timeout, ssl_options.merge( - connection_address: address, connection_generation: generation, pipe: options[:pipe])) + opts = ssl_options.merge( + connection_address: address, + connection_generation: generation, + pipe: options[:pipe], + connect_timeout: context&.remaining_timeout_sec, + csot: !!context&.csot? + ) + address.socket(socket_timeout, opts) end end diff --git a/lib/mongo/server/connection_base.rb b/lib/mongo/server/connection_base.rb index 709f1e920d..2803cb9c45 100644 --- a/lib/mongo/server/connection_base.rb +++ b/lib/mongo/server/connection_base.rb @@ -169,6 +169,7 @@ def deliver(message, context, options = {}) raise Error::LintError, "Trying to deliver a message over a disconnected connection (to #{address})" end buffer = serialize(message, context) + check_timeout!(context) ensure_connected do |socket| operation_id = Monitoring.next_operation_id started_event = command_started(address, operation_id, message.payload, @@ -181,9 +182,10 @@ def deliver(message, context, options = {}) result = nil begin result = add_server_diagnostics do - socket.write(buffer.to_s) + socket.write(buffer.to_s, timeout: context.remaining_timeout_sec) if message.replyable? - Protocol::Message.deserialize(socket, max_message_size, message.request_id, options) + check_timeout!(context) + Protocol::Message.deserialize(socket, max_message_size, message.request_id, options.merge(timeout: context.remaining_timeout_sec)) else nil end @@ -273,6 +275,24 @@ def serialize(message, context, buffer = BSON::ByteBuffer.new) buffer end + + # If timeoutMS is set for the operation context, checks whether there is + # enough time left to send the corresponding message to the server + # (remaining timeout is bigger than minimum round trip time for + # the server) + # + # @param [ Mongo::Operation::Context ] context Context of the operation. + # + # @raise [ Mongo::Error::TimeoutError ] if timeout expired or there is + # not enough time to send the message to the server. + def check_timeout!(context) + return if [nil, 0].include?(context.deadline) + + time_to_execute = context.remaining_timeout_sec - server.minimum_round_trip_time + if time_to_execute <= 0 + raise Mongo::Error::TimeoutError + end + end end end end diff --git a/lib/mongo/server/connection_pool.rb b/lib/mongo/server/connection_pool.rb index dfbc718522..4cc0c9a7e2 100644 --- a/lib/mongo/server/connection_pool.rb +++ b/lib/mongo/server/connection_pool.rb @@ -205,11 +205,18 @@ def min_size # The time to wait, in seconds, for a connection to become available. # + # @param [ Mongo::Operation:Context | nil ] context Context of the operation + # the connection is requested for, if any. + # # @return [ Float ] The queue wait timeout. # # @since 2.9.0 - def wait_timeout - @wait_timeout ||= options[:wait_timeout] || DEFAULT_WAIT_TIMEOUT + def wait_timeout(context = nil) + if context&.remaining_timeout_sec.nil? + options[:wait_timeout] || DEFAULT_WAIT_TIMEOUT + else + context&.remaining_timeout_sec + end end # The maximum seconds a socket can remain idle since it has been @@ -345,6 +352,10 @@ def summary # The returned connection counts toward the pool's max size. When the # caller is finished using the connection, the connection should be # checked back in via the check_in method. + # @param [ Integer | nil ] :connection_global_id The global id for the + # connection to check out. + # @param [ Mongo::Operation:Context | nil ] :context Context of the operation + # the connection is requested for, if any. # # @return [ Mongo::Server::Connection ] The checked out connection. # @raise [ Error::PoolClosedError ] If the pool has been closed. @@ -352,7 +363,7 @@ def summary # and remains so for longer than the wait timeout. # # @since 2.9.0 - def check_out(connection_global_id: nil) + def check_out(connection_global_id: nil, context: nil) check_invariants publish_cmap_event( @@ -362,7 +373,9 @@ def check_out(connection_global_id: nil) raise_if_pool_closed! raise_if_pool_paused_locked! - connection = retrieve_and_connect_connection(connection_global_id) + connection = retrieve_and_connect_connection( + connection_global_id, context + ) publish_cmap_event( Monitoring::Event::Cmap::ConnectionCheckedOut.new(@server.address, connection.id, self), @@ -698,10 +711,13 @@ def inspect # @return [ Object ] The result of the block. # # @since 2.0.0 - def with_connection(connection_global_id: nil) + def with_connection(connection_global_id: nil, context: nil) raise_if_closed! - connection = check_out(connection_global_id: connection_global_id) + connection = check_out( + connection_global_id: connection_global_id, + context: context + ) yield(connection) rescue Error::SocketError, Error::SocketTimeoutError, Error::ConnectionPerished => e maybe_raise_pool_cleared!(connection, e) @@ -975,9 +991,9 @@ def maybe_raise_pool_cleared!(connection, e) # Attempts to connect (handshake and auth) the connection. If an error is # encountered, closes the connection and raises the error. - def connect_connection(connection) + def connect_connection(connection, context = nil) begin - connection.connect! + connection.connect!(context) rescue Exception connection.disconnect!(reason: :error) raise @@ -1242,16 +1258,18 @@ def get_connection(pid, connection_global_id) # Retrieves a connection and connects it. # - # @param [ Integer ] connection_global_id The global id for the + # @param [ Integer | nil ] connection_global_id The global id for the # connection to check out. + # @param [ Mongo::Operation:Context | nil ] context Context of the operation + # the connection is requested for, if any. # # @return [ Mongo::Server::Connection ] The checked out connection. # # @raise [ Error::PoolClosedError ] If the pool has been closed. # @raise [ Timeout::Error ] If the connection pool is at maximum size # and remains so for longer than the wait timeout. - def retrieve_and_connect_connection(connection_global_id) - deadline = Utils.monotonic_time + wait_timeout + def retrieve_and_connect_connection(connection_global_id, context = nil) + deadline = Utils.monotonic_time + wait_timeout(context) connection = nil @lock.synchronize do @@ -1267,7 +1285,7 @@ def retrieve_and_connect_connection(connection_global_id) connection = wait_for_connection(connection_global_id, deadline) end - connect_or_raise(connection) unless connection.connected? + connect_or_raise(connection, context) unless connection.connected? @lock.synchronize do @checked_out_connections << connection @@ -1327,8 +1345,8 @@ def wait_for_connection(connection_global_id, deadline) # cannot be connected. # This method also publish corresponding event and ensures that counters # and condition variables are updated. - def connect_or_raise(connection) - connect_connection(connection) + def connect_or_raise(connection, context) + connect_connection(connection, context) rescue Exception # Handshake or authentication failed @lock.synchronize do diff --git a/lib/mongo/server/description.rb b/lib/mongo/server/description.rb index 3e21b6a635..20b1448721 100644 --- a/lib/mongo/server/description.rb +++ b/lib/mongo/server/description.rb @@ -209,8 +209,8 @@ class Description # @param [ Hash ] config The result of the hello command. # @param [ Float ] average_round_trip_time The moving average time (sec) the hello # command took to complete. - # @param [ Float ] average_round_trip_time The moving average time (sec) - # the ismaster call took to complete. + # @param [ Float ] minimum_round_trip_time The minimum round trip time + # of ten last hello commands. # @param [ true | false ] load_balancer Whether the server is treated as # a load balancer. # @param [ true | false ] force_load_balancer Whether the server is @@ -218,7 +218,8 @@ class Description # # @api private def initialize(address, config = {}, average_round_trip_time: nil, - load_balancer: false, force_load_balancer: false + minimum_round_trip_time: 0, load_balancer: false, + force_load_balancer: false ) @address = address @config = config @@ -226,6 +227,7 @@ def initialize(address, config = {}, average_round_trip_time: nil, @force_load_balancer = !!force_load_balancer @features = Features.new(wire_versions, me || @address.to_s) @average_round_trip_time = average_round_trip_time + @minimum_round_trip_time = minimum_round_trip_time @last_update_time = Time.now.freeze @last_update_monotime = Utils.monotonic_time @@ -302,6 +304,10 @@ def features # @return [ Float ] The moving average time the hello call took to complete. attr_reader :average_round_trip_time + # @return [ Float ] The minimum time from the ten last hello calls took + # to complete. + attr_reader :minimum_round_trip_time + # Returns whether this server is an arbiter, per the SDAM spec. # # @example Is the server an arbiter? @@ -723,8 +729,7 @@ def unknown? # @api private def ok? - config[Operation::Result::OK] && - config[Operation::Result::OK] == 1 || false + config[Operation::Result::OK] == 1 end # Get the range of supported wire versions for the server. @@ -802,6 +807,14 @@ def me_mismatch? !!(address.to_s.downcase != me.downcase if me) end + # Whether this description is from a mongocryptd server. + # + # @return [ true, false ] Whether this description is from a mongocryptd + # server. + def mongocryptd? + ok? && config['iscryptd'] == true + end + # opTime in lastWrite subdocument of the hello response. # # @return [ BSON::Timestamp ] The timestamp. diff --git a/lib/mongo/server/description/features.rb b/lib/mongo/server/description/features.rb index 97a222713b..cfae85c7a2 100644 --- a/lib/mongo/server/description/features.rb +++ b/lib/mongo/server/description/features.rb @@ -83,7 +83,7 @@ class Features # The wire protocol versions that this version of the driver supports. # # @since 2.0.0 - DRIVER_WIRE_VERSIONS = (6..21).freeze + DRIVER_WIRE_VERSIONS = (6..25).freeze # Create the methods for each mapping to tell if they are supported. # diff --git a/lib/mongo/server/monitor.rb b/lib/mongo/server/monitor.rb index c2ae278f1e..9130fe7128 100644 --- a/lib/mongo/server/monitor.rb +++ b/lib/mongo/server/monitor.rb @@ -237,8 +237,11 @@ def run_sdam_flow(result, awaited: false, scan_error: nil) @sdam_mutex.synchronize do old_description = server.description - new_description = Description.new(server.address, result, - average_round_trip_time: server.round_trip_time_averager.average_round_trip_time + new_description = Description.new( + server.address, + result, + average_round_trip_time: server.round_trip_time_calculator.average_round_trip_time, + minimum_round_trip_time: server.round_trip_time_calculator.minimum_round_trip_time ) server.cluster.run_sdam_flow(server.description, new_description, awaited: awaited, scan_error: scan_error) @@ -306,7 +309,7 @@ def check end if @connection - result = server.round_trip_time_averager.measure do + result = server.round_trip_time_calculator.measure do begin doc = @connection.check_document cmd = Protocol::Query.new( @@ -323,7 +326,7 @@ def check else connection = Connection.new(server.address, options) connection.connect! - result = server.round_trip_time_averager.measure do + result = server.round_trip_time_calculator.measure do connection.handshake! end @connection = connection diff --git a/lib/mongo/server/pending_connection.rb b/lib/mongo/server/pending_connection.rb index 1981e32812..5d2c62a6dd 100644 --- a/lib/mongo/server/pending_connection.rb +++ b/lib/mongo/server/pending_connection.rb @@ -120,7 +120,7 @@ def handshake_and_authenticate! # # @return [ Mongo::Protocol::Reply ] Deserialized server response. def get_handshake_response(hello_command) - @server.round_trip_time_averager.measure do + @server.round_trip_time_calculator.measure do add_server_diagnostics do socket.write(hello_command.serialize.to_s) Protocol::Message.deserialize(socket, Protocol::Message::MAX_MESSAGE_SIZE) @@ -168,7 +168,11 @@ def handshake!(speculative_auth_doc: nil) doc['serviceId'] ||= "fake:#{rand(2**32-1)+1}" end - post_handshake(doc, @server.round_trip_time_averager.average_round_trip_time) + post_handshake( + doc, + @server.round_trip_time_calculator.average_round_trip_time, + @server.round_trip_time_calculator.minimum_round_trip_time + ) doc end @@ -218,7 +222,7 @@ def ensure_connected # # @return [ Server::Description ] The server description calculated from # the handshake response for this particular connection. - def post_handshake(response, average_rtt) + def post_handshake(response, average_rtt, minimum_rtt) if response["ok"] == 1 # Auth mechanism is entirely dependent on the contents of # hello response *for this connection*. diff --git a/lib/mongo/server/round_trip_time_averager.rb b/lib/mongo/server/round_trip_time_calculator.rb similarity index 71% rename from lib/mongo/server/round_trip_time_averager.rb rename to lib/mongo/server/round_trip_time_calculator.rb index 1634691d54..5708dc2c95 100644 --- a/lib/mongo/server/round_trip_time_averager.rb +++ b/lib/mongo/server/round_trip_time_calculator.rb @@ -18,20 +18,30 @@ module Mongo class Server # @api private - class RoundTripTimeAverager + class RoundTripTimeCalculator # The weighting factor (alpha) for calculating the average moving # round trip time. RTT_WEIGHT_FACTOR = 0.2.freeze private_constant :RTT_WEIGHT_FACTOR + RTT_SAMPLES_FOR_MINIMUM = 10 + private_constant :RTT_SAMPLES_FOR_MINIMUM + + MIN_SAMPLES = 3 + private_constant :MIN_SAMPLES + def initialize @last_round_trip_time = nil @average_round_trip_time = nil + @minimum_round_trip_time = 0 + @lock = Mutex.new + @rtts = [] end attr_reader :last_round_trip_time attr_reader :average_round_trip_time + attr_reader :minimum_round_trip_time def measure start = Utils.monotonic_time @@ -44,14 +54,17 @@ def measure rescue Error, Error::AuthError => exc # For other errors, RTT is valid. end - last_round_trip_time = Utils.monotonic_time - start + last_rtt = Utils.monotonic_time - start # If hello fails, we need to return the last round trip time # because it is used in the heartbeat failed SDAM event, # but we must not update the round trip time recorded in the server. unless exc - @last_round_trip_time = last_round_trip_time - update_average_round_trip_time + @last_round_trip_time = last_rtt + @lock.synchronize do + update_average_round_trip_time + update_minimum_round_trip_time + end end if exc @@ -61,9 +74,6 @@ def measure end end - private - - # This method is separate for testing purposes. def update_average_round_trip_time @average_round_trip_time = if average_round_trip_time RTT_WEIGHT_FACTOR * last_round_trip_time + (1 - RTT_WEIGHT_FACTOR) * average_round_trip_time @@ -71,6 +81,14 @@ def update_average_round_trip_time last_round_trip_time end end + + def update_minimum_round_trip_time + @rtts.push(last_round_trip_time) unless last_round_trip_time.nil? + @minimum_round_trip_time = 0 and return if @rtts.size < MIN_SAMPLES + + @rtts.shift if @rtts.size > RTT_SAMPLES_FOR_MINIMUM + @minimum_round_trip_time = @rtts.compact.min + end end end end diff --git a/lib/mongo/server_selector/base.rb b/lib/mongo/server_selector/base.rb index 10eb478449..ae0a495440 100644 --- a/lib/mongo/server_selector/base.rb +++ b/lib/mongo/server_selector/base.rb @@ -33,11 +33,11 @@ class Base # # @option options [ Integer ] :local_threshold The local threshold boundary for # nearest selection in seconds. - # @option options [ Integer ] max_staleness The maximum replication lag, + # @option options [ Integer ] :max_staleness The maximum replication lag, # in seconds, that a secondary can suffer and still be eligible for a read. # A value of -1 is treated identically to nil, which is to not # have a maximum staleness. - # @option options [ Hash | nil ] hedge A Hash specifying whether to enable hedged + # @option options [ Hash | nil ] :hedge A Hash specifying whether to enable hedged # reads on the server. Hedged reads are not enabled by default. When # specifying this option, it must be in the format: { enabled: true }, # where the value of the :enabled key is a boolean value. @@ -168,6 +168,8 @@ def ==(other) # be selected from only if no other servers are available. This is # used to avoid selecting the same server twice in a row when # retrying a command. + # @param [ Float | nil ] :timeout Timeout in seconds for the operation, + # if any. # # @return [ Mongo::Server ] A server matching the server preference. # @@ -178,21 +180,35 @@ def ==(other) # lint mode is enabled. # # @since 2.0.0 - def select_server(cluster, ping = nil, session = nil, write_aggregation: false, deprioritized: []) - select_server_impl(cluster, ping, session, write_aggregation, deprioritized).tap do |server| + def select_server( + cluster, + ping = nil, + session = nil, + write_aggregation: false, + deprioritized: [], + timeout: nil + ) + select_server_impl(cluster, ping, session, write_aggregation, deprioritized, timeout).tap do |server| if Lint.enabled? && !server.pool.ready? raise Error::LintError, 'Server selector returning a server with a pool which is not ready' end end end - # Parameters and return values are the same as for select_server. - private def select_server_impl(cluster, ping, session, write_aggregation, deprioritized) + # Parameters and return values are the same as for select_server, only + # the +timeout+ param is renamed to +csot_timeout+. + private def select_server_impl(cluster, ping, session, write_aggregation, deprioritized, csot_timeout) if cluster.topology.is_a?(Cluster::Topology::LoadBalanced) return cluster.servers.first end - server_selection_timeout = cluster.options[:server_selection_timeout] || SERVER_SELECTION_TIMEOUT + timeout = cluster.options[:server_selection_timeout] || SERVER_SELECTION_TIMEOUT + + server_selection_timeout = if csot_timeout && csot_timeout > 0 + [timeout, csot_timeout].min + else + timeout + end # Special handling for zero timeout: if we have to select a server, # and the timeout is zero, fail immediately (since server selection @@ -638,9 +654,9 @@ def validate_max_staleness_value!(cluster) # state resulting from SDAM will immediately wake up this method and # cause it to return. # - # If the cluster des not have a server selection semaphore, waits + # If the cluster does not have a server selection semaphore, waits # the smaller of 0.25 seconds and the specified remaining time. - # This functionality is provided for backwards compatibilty only for + # This functionality is provided for backwards compatibility only for # applications directly invoking the server selection process. # If lint mode is enabled and the cluster does not have a server # selection semaphore, Error::LintError will be raised. diff --git a/lib/mongo/session.rb b/lib/mongo/session.rb index b22519efc1..be9c1f2a42 100644 --- a/lib/mongo/session.rb +++ b/lib/mongo/session.rb @@ -57,6 +57,12 @@ class Session # # @option options [ true|false ] :causal_consistency Whether to enable # causal consistency for this session. + # @option options [ Integer ] :default_timeout_ms The timeoutMS value for + # the following operations executed on the session: + # - commitTransaction + # - abortTransaction + # - withTransaction + # - endSession # @option options [ Hash ] :default_transaction_options Options to pass # to start_transaction by default, can contain any of the options that # start_transaction accepts. @@ -96,6 +102,7 @@ def initialize(server_session, client, options = {}) @options = options.dup.freeze @cluster_time = nil @state = NO_TRANSACTION_STATE + @with_transaction_deadline = nil end # @return [ Hash ] The options for this session. @@ -438,9 +445,21 @@ def end_session # progress or if the write concern is unacknowledged. # # @since 2.7.0 - def with_transaction(options=nil) - # Non-configurable 120 second timeout for the entire operation - deadline = Utils.monotonic_time + 120 + def with_transaction(options = nil) + if timeout_ms = (options || {})[:timeout_ms] + timeout_sec = timeout_ms / 1_000.0 + deadline = Utils.monotonic_time + timeout_sec + @with_transaction_deadline = deadline + elsif default_timeout_ms = @options[:default_timeout_ms] + timeout_sec = default_timeout_ms / 1_000.0 + deadline = Utils.monotonic_time + timeout_sec + @with_transaction_deadline = deadline + elsif @client.timeout_sec + deadline = Utils.monotonic_time + @client.timeout_sec + @with_transaction_deadline = deadline + else + deadline = Utils.monotonic_time + 120 + end transaction_in_progress = false loop do commit_options = {} @@ -454,6 +473,7 @@ def with_transaction(options=nil) rescue Exception => e if within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE) log_warn("Aborting transaction due to #{e.class}: #{e}") + @with_transaction_deadline = nil abort_transaction transaction_in_progress = false end @@ -481,7 +501,7 @@ def with_transaction(options=nil) rescue Mongo::Error => e if e.label?('UnknownTransactionCommitResult') if Utils.monotonic_time >= deadline || - e.is_a?(Error::OperationFailure) && e.max_time_ms_expired? + e.is_a?(Error::OperationFailure::Family) && e.max_time_ms_expired? then transaction_in_progress = false raise @@ -522,9 +542,10 @@ def with_transaction(options=nil) log_warn('with_transaction callback broke out of with_transaction loop, aborting transaction') begin abort_transaction - rescue Error::OperationFailure, Error::InvalidTransactionOperation + rescue Error::OperationFailure::Family, Error::InvalidTransactionOperation end end + @with_transaction_deadline = nil end # Places subsequent operations in this session into a new transaction. @@ -539,6 +560,7 @@ def with_transaction(options=nil) # # @option options [ Integer ] :max_commit_time_ms The maximum amount of # time to allow a single commitTransaction command to run, in milliseconds. + # This options is deprecated, use :timeout_ms instead. # @option options [ Hash ] :read_concern The read concern options hash, # with the following optional keys: # - *:level* -- the read preference level as a symbol; valid values @@ -549,6 +571,10 @@ def with_transaction(options=nil) # items: # - *:mode* -- read preference specified as a symbol; the only valid value is # *:primary*. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the client. # # @raise [ Error::InvalidTransactionOperation ] If a transaction is already in # progress or if the write concern is unacknowledged. @@ -611,6 +637,10 @@ def start_transaction(options = nil) # # @option options :write_concern [ nil | WriteConcern::Base ] The write # concern to use for this operation. + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the client. # # @raise [ Error::InvalidTransactionOperation ] If there is no active transaction. # @@ -647,7 +677,11 @@ def commit_transaction(options=nil) write_concern = WriteConcern.get(write_concern) end - context = Operation::Context.new(client: @client, session: self) + context = Operation::Context.new( + client: @client, + session: self, + operation_timeouts: operation_timeouts(options) + ) write_with_retry(write_concern, ending_transaction: true, context: context, ) do |connection, txn_num, context| @@ -685,10 +719,15 @@ def commit_transaction(options=nil) # @example Abort the transaction. # session.abort_transaction # + # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds. + # Must be a non-negative integer. An explicit value of 0 means infinite. + # The default value is unset which means the value is inherited from + # the client. + # # @raise [ Error::InvalidTransactionOperation ] If there is no active transaction. # # @since 2.6.0 - def abort_transaction + def abort_transaction(options = nil) QueryCache.clear check_if_ended! @@ -705,10 +744,16 @@ def abort_transaction Mongo::Error::InvalidTransactionOperation.cannot_call_twice_msg(:abortTransaction)) end + options ||= {} + begin unless starting_transaction? @aborting_transaction = true - context = Operation::Context.new(client: @client, session: self) + context = Operation::Context.new( + client: @client, + session: self, + operation_timeouts: operation_timeouts(options) + ) write_with_retry(txn_options[:write_concern], ending_transaction: true, context: context, ) do |connection, txn_num, context| @@ -898,7 +943,7 @@ def add_txn_num!(command) # # @since 2.6.0 # @api private - def add_txn_opts!(command, read) + def add_txn_opts!(command, read, context) command.tap do |c| # The read concern should be added to any command that starts a transaction. if starting_transaction? @@ -952,6 +997,14 @@ def add_txn_opts!(command, read) if c[:writeConcern] && c[:writeConcern][:w] && c[:writeConcern][:w].is_a?(Symbol) c[:writeConcern][:w] = c[:writeConcern][:w].to_s end + + # Ignore wtimeout if csot + if context&.csot? + c[:writeConcern]&.delete(:wtimeout) + end + + # We must not send an empty (server default) write concern. + c.delete(:writeConcern) if c[:writeConcern]&.empty? end end @@ -1138,6 +1191,8 @@ def txn_num # @api private attr_accessor :snapshot_timestamp + attr_reader :with_transaction_deadline + private # Get the read concern the session will use when starting a transaction. @@ -1217,5 +1272,19 @@ def check_transactions_supported! end end end + + def operation_timeouts(opts) + { + inherited_timeout_ms: @client.timeout_ms + }.tap do |result| + if @with_transaction_deadline.nil? + if timeout_ms = opts[:timeout_ms] + result[:operation_timeout_ms] = timeout_ms + elsif default_timeout_ms = options[:default_timeout_ms] + result[:operation_timeout_ms] = default_timeout_ms + end + end + end + end end end diff --git a/lib/mongo/socket.rb b/lib/mongo/socket.rb index 62e3ef464e..3134f2aa87 100644 --- a/lib/mongo/socket.rb +++ b/lib/mongo/socket.rb @@ -192,27 +192,24 @@ def gets(*args) # socket.read(4096) # # @param [ Integer ] length The number of bytes to read. - # @param [ Numeric ] timeout The timeout to use for each chunk read. + # @param [ Numeric ] socket_timeout The timeout to use for each chunk read, + # mutually exclusive to +timeout+. + # @param [ Numeric ] timeout The total timeout to the whole read operation, + # mutually exclusive to +socket_timeout+. # # @raise [ Mongo::SocketError ] If not all data is returned. # # @return [ Object ] The data from the socket. # # @since 2.0.0 - def read(length, timeout: nil) - map_exceptions do - data = read_from_socket(length, timeout: timeout) - unless (data.length > 0 || length == 0) - raise IOError, "Expected to read > 0 bytes but read 0 bytes" - end - while data.length < length - chunk = read_from_socket(length - data.length, timeout: timeout) - unless (chunk.length > 0 || length == 0) - raise IOError, "Expected to read > 0 bytes but read 0 bytes" - end - data << chunk - end - data + def read(length, socket_timeout: nil, timeout: nil) + if !socket_timeout.nil? && !timeout.nil? + raise ArgumentError, 'Both timeout and socket_timeout cannot be set' + end + if !socket_timeout.nil? || timeout.nil? + read_without_timeout(length, socket_timeout) + else + read_with_timeout(length, timeout) end end @@ -233,15 +230,16 @@ def readbyte # Writes data to the socket instance. # # @param [ Array ] args The data to be written. + # @param [ Numeric ] timeout The total timeout to the whole write operation. # # @return [ Integer ] The length of bytes written to the socket. # # @raise [ Error::SocketError | Error::SocketTimeoutError ] When there is a network error during the write. # # @since 2.0.0 - def write(*args) + def write(*args, timeout: nil) map_exceptions do - do_write(*args) + do_write(*args, timeout: timeout) end end @@ -254,7 +252,7 @@ def eof? true end - # For backwards compatibilty only, do not use. + # For backwards compatibility only, do not use. # # @return [ true ] Always true. # @@ -265,18 +263,76 @@ def connectable? private - def read_from_socket(length, timeout: nil) + # Reads the +length+ bytes from the socket, the read operation duration is + # limited to +timeout+ second. + # + # @param [ Integer ] length The number of bytes to read. + # @param [ Numeric ] timeout The total timeout to the whole read operation. + # + # @return [ Object ] The data from the socket. + def read_with_timeout(length, timeout) + deadline = Utils.monotonic_time + timeout + map_exceptions do + String.new.tap do |data| + while data.length < length + socket_timeout = deadline - Utils.monotonic_time + if socket_timeout <= 0 + raise Mongo::Error::TimeoutError + end + chunk = read_from_socket(length - data.length, socket_timeout: socket_timeout, csot: true) + unless chunk.length > 0 + raise IOError, "Expected to read > 0 bytes but read 0 bytes" + end + data << chunk + end + end + end + end + + # Reads the +length+ bytes from the socket. The read operation may involve + # multiple socket reads, each read is limited to +timeout+ second, + # if the parameter is provided. + # + # @param [ Integer ] length The number of bytes to read. + # @param [ Numeric ] socket_timeout The timeout to use for each chunk read. + # + # @return [ Object ] The data from the socket. + def read_without_timeout(length, socket_timeout = nil) + map_exceptions do + String.new.tap do |data| + while data.length < length + chunk = read_from_socket(length - data.length, socket_timeout: socket_timeout) + unless chunk.length > 0 + raise IOError, "Expected to read > 0 bytes but read 0 bytes" + end + data << chunk + end + end + end + end + + + # Reads the +length+ bytes from the socket. The read operation may involve + # multiple socket reads, each read is limited to +timeout+ second, + # if the parameter is provided. + # + # @param [ Integer ] length The number of bytes to read. + # @param [ Numeric ] :socket_timeout The timeout to use for each chunk read. + # @param [ true | false ] :csot Whether the CSOT timeout is set for the operation. + # + # @return [ Object ] The data from the socket. + def read_from_socket(length, socket_timeout: nil, csot: false) # Just in case if length == 0 return ''.force_encoding('BINARY') end - _timeout = timeout || self.timeout + _timeout = socket_timeout || self.timeout if _timeout if _timeout > 0 deadline = Utils.monotonic_time + _timeout elsif _timeout < 0 - raise Errno::ETIMEDOUT, "Negative timeout #{_timeout} given to socket" + raise_timeout_error!("Negative timeout #{_timeout} given to socket", csot) end end @@ -331,7 +387,7 @@ def read_from_socket(length, timeout: nil) if deadline select_timeout = deadline - Utils.monotonic_time if select_timeout <= 0 - raise Errno::ETIMEDOUT, "Took more than #{_timeout} seconds to receive data" + raise_timeout_error!("Took more than #{_timeout} seconds to receive data", csot) end end pipe = options[:pipe] @@ -373,11 +429,11 @@ def read_from_socket(length, timeout: nil) if deadline select_timeout = deadline - Utils.monotonic_time if select_timeout <= 0 - raise Errno::ETIMEDOUT, "Took more than #{_timeout} seconds to receive data" + raise_timeout_error!("Took more than #{_timeout} seconds to receive data", csot) end end elsif rv.nil? - raise Errno::ETIMEDOUT, "Took more than #{_timeout} seconds to receive data (select call timed out)" + raise_timeout_error!("Took more than #{_timeout} seconds to receive data (select call timed out)", csot) end retry end @@ -402,9 +458,23 @@ def read_buffer_size # sholud map exceptions. # # @param [ Array ] args The data to be written. + # @param [ Numeric ] :timeout The total timeout to the whole write operation. + # + # @return [ Integer ] The length of bytes written to the socket. + def do_write(*args, timeout: nil) + if timeout.nil? + write_without_timeout(*args) + else + write_with_timeout(*args, timeout: timeout) + end + end + + # Writes data to to the socket. + # + # @param [ Array ] args The data to be written. # # @return [ Integer ] The length of bytes written to the socket. - def do_write(*args) + def write_without_timeout(*args) # This method used to forward arguments to @socket.write in a # single call like so: # @@ -428,6 +498,57 @@ def do_write(*args) end end + # Writes data to to the socket, the write duration is limited to +timeout+. + # + # @param [ Array ] args The data to be written. + # @param [ Numeric ] :timeout The total timeout to the whole write operation. + # + # @return [ Integer ] The length of bytes written to the socket. + def write_with_timeout(*args, timeout:) + raise ArgumentError, 'timeout cannot be nil' if timeout.nil? + raise_timeout_error!("Negative timeout #{timeout} given to socket", true) if timeout < 0 + + written = 0 + args.each do |buf| + buf = buf.to_s + i = 0 + while i < buf.length + chunk = buf[i...(i + WRITE_CHUNK_SIZE)] + written += write_chunk(chunk, timeout) + i += WRITE_CHUNK_SIZE + end + end + written + end + + def write_chunk(chunk, timeout) + deadline = Utils.monotonic_time + timeout + written = 0 + begin + written += @socket.write_nonblock(chunk[written..-1]) + rescue IO::WaitWritable, Errno::EINTR + select_timeout = deadline - Utils.monotonic_time + rv = Kernel.select(nil, [@socket], nil, select_timeout) + if BSON::Environment.jruby? + # Ignore the return value of Kernel.select. + # On JRuby, select appears to return nil prior to timeout expiration + # (apparently due to a EAGAIN) which then causes us to fail the read + # even though we could have retried it. + # Check the deadline ourselves. + if deadline + select_timeout = deadline - Utils.monotonic_time + if select_timeout <= 0 + raise_timeout_error!("Took more than #{timeout} seconds to receive data", true) + end + end + elsif rv.nil? + raise_timeout_error!("Took more than #{timeout} seconds to receive data (select call timed out)", true) + end + retry + end + written + end + def unix_socket?(sock) defined?(UNIXSocket) && sock.is_a?(UNIXSocket) end @@ -482,5 +603,13 @@ def map_exceptions def human_address raise NotImplementedError end + + def raise_timeout_error!(message = nil, csot = false) + if csot + raise Mongo::Error::TimeoutError + else + raise Errno::ETIMEDOUT, message + end + end end end diff --git a/lib/mongo/socket/ssl.rb b/lib/mongo/socket/ssl.rb index 10bb7b4d6c..d9e5d7cb52 100644 --- a/lib/mongo/socket/ssl.rb +++ b/lib/mongo/socket/ssl.rb @@ -142,26 +142,37 @@ def initialize(host, port, host_name, timeout, family, options = {}) # # @since 2.0.0 def connect! - Timeout.timeout(options[:connect_timeout], Error::SocketTimeoutError, "The socket took over #{options[:connect_timeout]} seconds to connect") do - map_exceptions do - @tcp_socket.connect(::Socket.pack_sockaddr_in(port, host)) - end - @socket = OpenSSL::SSL::SSLSocket.new(@tcp_socket, context) - begin - @socket.hostname = @host_name - @socket.sync_close = true - map_exceptions do - @socket.connect + sockaddr = ::Socket.pack_sockaddr_in(port, host) + connect_timeout = options[:connect_timeout] + map_exceptions do + if connect_timeout && connect_timeout != 0 + deadline = Utils.monotonic_time + connect_timeout + if BSON::Environment.jruby? + # We encounter some strange problems with connect_nonblock for + # ssl sockets on JRuby. Therefore, we use the old +Timeout.timeout+ + # solution, even though it is known to be not very reliable. + raise Error::SocketTimeoutError, 'connect_timeout expired' if connect_timeout < 0 + + Timeout.timeout(connect_timeout, Error::SocketTimeoutError, "The socket took over #{options[:connect_timeout]} seconds to connect") do + connect_without_timeout(sockaddr) + end + else + connect_with_timeout(sockaddr, connect_timeout) end + remaining_timeout = deadline - Utils.monotonic_time + verify_certificate!(@socket) + verify_ocsp_endpoint!(@socket, remaining_timeout) + else + connect_without_timeout(sockaddr) verify_certificate!(@socket) verify_ocsp_endpoint!(@socket) - rescue - @socket.close - @socket = nil - raise end - self end + self + rescue + @socket&.close + @socket = nil + raise end private :connect! @@ -182,6 +193,87 @@ def readbyte private + # Connects the socket without a timeout provided. + # + # @param [ String ] sockaddr Address to connect to. + def connect_without_timeout(sockaddr) + @tcp_socket.connect(sockaddr) + @socket = OpenSSL::SSL::SSLSocket.new(@tcp_socket, context) + @socket.hostname = @host_name + @socket.sync_close = true + @socket.connect + end + + # Connects the socket with the connect timeout. The timeout applies to + # connecting both ssl socket and the underlying tcp socket. + # + # @param [ String ] sockaddr Address to connect to. + def connect_with_timeout(sockaddr, connect_timeout) + if connect_timeout <= 0 + raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect" + end + + deadline = Utils.monotonic_time + connect_timeout + connect_tcp_socket_with_timeout(sockaddr, deadline, connect_timeout) + connnect_ssl_socket_with_timeout(deadline, connect_timeout) + end + + def connect_tcp_socket_with_timeout(sockaddr, deadline, connect_timeout) + if deadline <= Utils.monotonic_time + raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect" + end + begin + @tcp_socket.connect_nonblock(sockaddr) + rescue IO::WaitWritable + with_select_timeout(deadline, connect_timeout) do |select_timeout| + IO.select(nil, [@tcp_socket], nil, select_timeout) + end + retry + rescue Errno::EISCONN + # Socket is connected, nothing to do. + end + end + + def connnect_ssl_socket_with_timeout(deadline, connect_timeout) + if deadline <= Utils.monotonic_time + raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect" + end + @socket = OpenSSL::SSL::SSLSocket.new(@tcp_socket, context) + @socket.hostname = @host_name + @socket.sync_close = true + + # We still have time, connecting ssl socket. + begin + @socket.connect_nonblock + rescue IO::WaitReadable, OpenSSL::SSL::SSLErrorWaitReadable + with_select_timeout(deadline, connect_timeout) do |select_timeout| + IO.select([@socket], nil, nil, select_timeout) + end + retry + rescue IO::WaitWritable, OpenSSL::SSL::SSLErrorWaitWritable + with_select_timeout(deadline, connect_timeout) do |select_timeout| + IO.select(nil, [@socket], nil, select_timeout) + end + retry + rescue Errno::EISCONN + # Socket is connected, nothing to do + end + end + + # Raises +Error::SocketTimeoutError+ exception if deadline reached or the + # block returns nil. The block should call +IO.select+ with the + # +connect_timeout+ value. It returns nil if the +connect_timeout+ expires. + def with_select_timeout(deadline, connect_timeout, &block) + select_timeout = deadline - Utils.monotonic_time + if select_timeout <= 0 + raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect" + end + rv = block.call(select_timeout) + if rv.nil? + raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect" + end + end + def verify_certificate? # If ssl_verify_certificate is not present, disable only if # ssl_verify is explicitly set to false. @@ -362,7 +454,7 @@ def verify_certificate!(socket) end end - def verify_ocsp_endpoint!(socket) + def verify_ocsp_endpoint!(socket, timeout = nil) unless verify_ocsp_endpoint? return end @@ -371,7 +463,7 @@ def verify_ocsp_endpoint!(socket) ca_cert = socket.peer_cert_chain.last verifier = OcspVerifier.new(@host_name, cert, ca_cert, context.cert_store, - **Utils.shallow_symbolize_keys(options)) + **Utils.shallow_symbolize_keys(options).merge(timeout: timeout)) verifier.verify_with_cache end diff --git a/lib/mongo/socket/tcp.rb b/lib/mongo/socket/tcp.rb index 155c4ffc4a..61ece439f4 100644 --- a/lib/mongo/socket/tcp.rb +++ b/lib/mongo/socket/tcp.rb @@ -79,16 +79,50 @@ def initialize(host, port, timeout, family, options = {}) # @return [ TCP ] The connected socket instance. # # @since 2.0.0 + # @api private def connect! - Timeout.timeout(options[:connect_timeout], Error::SocketTimeoutError, "The socket took over #{options[:connect_timeout]} seconds to connect") do - socket.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1) - map_exceptions do - socket.connect(::Socket.pack_sockaddr_in(port, host)) + socket.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1) + sockaddr = ::Socket.pack_sockaddr_in(port, host) + connect_timeout = options[:connect_timeout] + map_exceptions do + if connect_timeout && connect_timeout != 0 + connect_with_timeout(sockaddr, connect_timeout) + else + connect_without_timeout(sockaddr) + end + end + self + end + + # @api private + def connect_without_timeout(sockaddr) + socket.connect(sockaddr) + end + + # @api private + def connect_with_timeout(sockaddr, connect_timeout) + if connect_timeout <= 0 + raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect" + end + + deadline = Utils.monotonic_time + connect_timeout + begin + socket.connect_nonblock(sockaddr) + rescue IO::WaitWritable + select_timeout = deadline - Utils.monotonic_time + if select_timeout <= 0 + raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect" + end + if IO.select(nil, [socket], nil, select_timeout) + retry + else + socket.close + raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect" end - self + rescue Errno::EISCONN + # Socket is connected, nothing more to do end end - private :connect! private diff --git a/lib/mongo/uri/options_mapper.rb b/lib/mongo/uri/options_mapper.rb index bc7e8f16dc..eac2f59c3b 100644 --- a/lib/mongo/uri/options_mapper.rb +++ b/lib/mongo/uri/options_mapper.rb @@ -272,6 +272,7 @@ def self.uri_option(uri_key, name, **extra) uri_option 'localThresholdMS', :local_threshold, type: :ms uri_option 'heartbeatFrequencyMS', :heartbeat_frequency, type: :ms uri_option 'maxIdleTimeMS', :max_idle_time, type: :ms + uri_option 'timeoutMS', :timeout_ms, type: :integer # Write Options uri_option 'w', :w, group: :write_concern, type: :w diff --git a/lib/mongo/version.rb b/lib/mongo/version.rb index df2b2a7904..a78e0fa94b 100644 --- a/lib/mongo/version.rb +++ b/lib/mongo/version.rb @@ -16,5 +16,5 @@ module Mongo # The current version of the driver. - VERSION = '2.20.1' + VERSION = '2.21.0' end diff --git a/spec/atlas/atlas_connectivity_spec.rb b/spec/atlas/atlas_connectivity_spec.rb index ecd511ffb3..07a0fd8a2a 100644 --- a/spec/atlas/atlas_connectivity_spec.rb +++ b/spec/atlas/atlas_connectivity_spec.rb @@ -10,6 +10,10 @@ require_atlas describe 'connection to Atlas' do + after do + client.close + end + it 'runs ismaster successfully' do expect { client.database.command(:hello => 1) } .not_to raise_error diff --git a/spec/atlas/operations_spec.rb b/spec/atlas/operations_spec.rb index 8a46ab3702..bee0e6021c 100644 --- a/spec/atlas/operations_spec.rb +++ b/spec/atlas/operations_spec.rb @@ -10,6 +10,10 @@ require_atlas describe 'ping' do + after do + client.close + end + it 'works' do expect do client.database.command(ping: 1) diff --git a/spec/integration/client_side_encryption/auto_encryption_mongocryptd_spawn_spec.rb b/spec/integration/client_side_encryption/auto_encryption_mongocryptd_spawn_spec.rb index 791c10d2f4..a73a35d9d5 100644 --- a/spec/integration/client_side_encryption/auto_encryption_mongocryptd_spawn_spec.rb +++ b/spec/integration/client_side_encryption/auto_encryption_mongocryptd_spawn_spec.rb @@ -57,7 +57,8 @@ 'jsonSchema' => kind_of(Hash), 'isRemoteSchema' => false, ), - { execution_options: { deserialize_as_bson: true } }, + { execution_options: { deserialize_as_bson: true }, timeout_ms: nil }, + ) .and_raise(Mongo::Error::NoServerAvailable.new(server_selector, cluster)) end diff --git a/spec/integration/client_side_encryption/auto_encryption_spec.rb b/spec/integration/client_side_encryption/auto_encryption_spec.rb index 636915c4df..de7b891ba9 100644 --- a/spec/integration/client_side_encryption/auto_encryption_spec.rb +++ b/spec/integration/client_side_encryption/auto_encryption_spec.rb @@ -30,7 +30,8 @@ extra_options: extra_options, }, database: 'auto_encryption', - max_pool_size: max_pool_size + max_pool_size: max_pool_size, + timeout_ms: timeout_ms ), ) end @@ -97,27 +98,84 @@ end shared_examples 'an encrypted command' do - context 'with AWS KMS provider' do - include_context 'with AWS kms_providers' + # context 'with AWS KMS provider' do + # include_context 'with AWS kms_providers' + + # context 'with validator' do + # include_context 'jsonSchema validator on collection' + # it_behaves_like 'it performs an encrypted command' + # end + + # context 'with schema map' do + # include_context 'schema map in client options' + # it_behaves_like 'it performs an encrypted command' + + # context 'with limited connection pool' do + # include_context 'limited connection pool' + # it_behaves_like 'it performs an encrypted command' + # end + # end + # end + + # context 'with Azure KMS provider' do + # include_context 'with Azure kms_providers' + + # context 'with validator' do + # include_context 'jsonSchema validator on collection' + # it_behaves_like 'it performs an encrypted command' + # end + + # context 'with schema map' do + # include_context 'schema map in client options' + # it_behaves_like 'it performs an encrypted command' + + # context 'with limited connection pool' do + # include_context 'limited connection pool' + # it_behaves_like 'it performs an encrypted command' + # end + # end + # end + + # context 'with GCP KMS provider' do + # include_context 'with GCP kms_providers' + + # context 'with validator' do + # include_context 'jsonSchema validator on collection' + # it_behaves_like 'it performs an encrypted command' + # end + + # context 'with schema map' do + # include_context 'schema map in client options' + # it_behaves_like 'it performs an encrypted command' + + # context 'with limited connection pool' do + # include_context 'limited connection pool' + # it_behaves_like 'it performs an encrypted command' + # end + # end + # end + + # context 'with KMIP KMS provider' do + # include_context 'with KMIP kms_providers' + + # context 'with validator' do + # include_context 'jsonSchema validator on collection' + # it_behaves_like 'it performs an encrypted command' + # end + + # context 'with schema map' do + # include_context 'schema map in client options' + # it_behaves_like 'it performs an encrypted command' + + # context 'with limited connection pool' do + # include_context 'limited connection pool' + # it_behaves_like 'it performs an encrypted command' + # end + # end + # end - context 'with validator' do - include_context 'jsonSchema validator on collection' - it_behaves_like 'it performs an encrypted command' - end - - context 'with schema map' do - include_context 'schema map in client options' - it_behaves_like 'it performs an encrypted command' - - context 'with limited connection pool' do - include_context 'limited connection pool' - it_behaves_like 'it performs an encrypted command' - end - end - end - - context 'with Azure KMS provider' do - include_context 'with Azure kms_providers' + context 'with local KMS provider' do + include_context 'with local kms_providers' context 'with validator' do include_context 'jsonSchema validator on collection' @@ -134,614 +192,563 @@ end end end + end - context 'with GCP KMS provider' do - include_context 'with GCP kms_providers' + [nil, 0].each do |timeout_ms| + context "with timeout_ms #{timeout_ms}" do + let(:timeout_ms) { timeout_ms } - context 'with validator' do - include_context 'jsonSchema validator on collection' - it_behaves_like 'it performs an encrypted command' - end + describe '#aggregate' do + shared_examples 'it performs an encrypted command' do + include_context 'encrypted document in collection' - context 'with schema map' do - include_context 'schema map in client options' - it_behaves_like 'it performs an encrypted command' + let(:result) do + encryption_client['users'].aggregate([ + { '$match' => { 'ssn' => ssn } } + ]).first + end - context 'with limited connection pool' do - include_context 'limited connection pool' - it_behaves_like 'it performs an encrypted command' - end - end - end + it 'encrypts the command and decrypts the response' do + result.should_not be_nil + result['ssn'].should == ssn + end - context 'with KMIP KMS provider' do - include_context 'with KMIP kms_providers' + context 'when bypass_auto_encryption=true' do + include_context 'bypass auto encryption' - context 'with validator' do - include_context 'jsonSchema validator on collection' - it_behaves_like 'it performs an encrypted command' - end + it 'does not encrypt the command' do + result.should be_nil + end - context 'with schema map' do - include_context 'schema map in client options' - it_behaves_like 'it performs an encrypted command' + it 'does auto decrypt the response' do + result = encryption_client['users'].aggregate([ + { '$match' => { 'ssn' => encrypted_ssn_binary } } + ]).first - context 'with limited connection pool' do - include_context 'limited connection pool' - it_behaves_like 'it performs an encrypted command' + result.should_not be_nil + result['ssn'].should == ssn + end + end end - end - end - context 'with local KMS provider' do - include_context 'with local kms_providers' - - context 'with validator' do - include_context 'jsonSchema validator on collection' - it_behaves_like 'it performs an encrypted command' + it_behaves_like 'an encrypted command' end - context 'with schema map' do - include_context 'schema map in client options' - it_behaves_like 'it performs an encrypted command' + describe '#count' do + shared_examples 'it performs an encrypted command' do + include_context 'multiple encrypted documents in collection' - context 'with limited connection pool' do - include_context 'limited connection pool' - it_behaves_like 'it performs an encrypted command' - end - end - end - end + let(:result) { encryption_client['users'].count(ssn: ssn) } - describe '#aggregate' do - shared_examples 'it performs an encrypted command' do - include_context 'encrypted document in collection' - - let(:result) do - encryption_client['users'].aggregate([ - { '$match' => { 'ssn' => ssn } } - ]).first - end - - it 'encrypts the command and decrypts the response' do - result.should_not be_nil - result['ssn'].should == ssn - end + it 'encrypts the command and finds the documents' do + expect(result).to eq(2) + end - context 'when bypass_auto_encryption=true' do - include_context 'bypass auto encryption' + context 'with bypass_auto_encryption=true' do + include_context 'bypass auto encryption' - it 'does not encrypt the command' do - result.should be_nil + it 'does not encrypt the command' do + expect(result).to eq(0) + end + end end - it 'does auto decrypt the response' do - result = encryption_client['users'].aggregate([ - { '$match' => { 'ssn' => encrypted_ssn_binary } } - ]).first - - result.should_not be_nil - result['ssn'].should == ssn - end + it_behaves_like 'an encrypted command' end - end - - it_behaves_like 'an encrypted command' - end - describe '#count' do - shared_examples 'it performs an encrypted command' do - include_context 'multiple encrypted documents in collection' + describe '#distinct' do + shared_examples 'it performs an encrypted command' do + include_context 'encrypted document in collection' - let(:result) { encryption_client['users'].count(ssn: ssn) } + let(:result) { encryption_client['users'].distinct(:ssn) } - it 'encrypts the command and finds the documents' do - expect(result).to eq(2) - end + it 'decrypts the SSN field' do + expect(result.length).to eq(1) + expect(result).to include(ssn) + end - context 'with bypass_auto_encryption=true' do - include_context 'bypass auto encryption' + context 'with bypass_auto_encryption=true' do + include_context 'bypass auto encryption' - it 'does not encrypt the command' do - expect(result).to eq(0) + it 'still decrypts the SSN field' do + expect(result.length).to eq(1) + expect(result).to include(ssn) + end + end end - end - end - - it_behaves_like 'an encrypted command' - end - describe '#distinct' do - shared_examples 'it performs an encrypted command' do - include_context 'encrypted document in collection' - - let(:result) { encryption_client['users'].distinct(:ssn) } - - it 'decrypts the SSN field' do - expect(result.length).to eq(1) - expect(result).to include(ssn) + it_behaves_like 'an encrypted command' end - context 'with bypass_auto_encryption=true' do - include_context 'bypass auto encryption' - - it 'still decrypts the SSN field' do - expect(result.length).to eq(1) - expect(result).to include(ssn) - end - end - end - - it_behaves_like 'an encrypted command' - end - - describe '#delete_one' do - shared_examples 'it performs an encrypted command' do - include_context 'encrypted document in collection' + describe '#delete_one' do + shared_examples 'it performs an encrypted command' do + include_context 'encrypted document in collection' - let(:result) { encryption_client['users'].delete_one(ssn: ssn) } + let(:result) { encryption_client['users'].delete_one(ssn: ssn) } - it 'encrypts the SSN field' do - expect(result.deleted_count).to eq(1) - end + it 'encrypts the SSN field' do + expect(result.deleted_count).to eq(1) + end - context 'with bypass_auto_encryption=true' do - include_context 'bypass auto encryption' + context 'with bypass_auto_encryption=true' do + include_context 'bypass auto encryption' - it 'does not encrypt the SSN field' do - expect(result.deleted_count).to eq(0) + it 'does not encrypt the SSN field' do + expect(result.deleted_count).to eq(0) + end + end end - end - end - it_behaves_like 'an encrypted command' - end - - describe '#delete_many' do - shared_examples 'it performs an encrypted command' do - include_context 'multiple encrypted documents in collection' - - let(:result) { encryption_client['users'].delete_many(ssn: ssn) } - - it 'decrypts the SSN field' do - expect(result.deleted_count).to eq(2) + it_behaves_like 'an encrypted command' end - context 'with bypass_auto_encryption=true' do - include_context 'bypass auto encryption' + describe '#delete_many' do + shared_examples 'it performs an encrypted command' do + include_context 'multiple encrypted documents in collection' - it 'does not encrypt the SSN field' do - expect(result.deleted_count).to eq(0) - end - end - end + let(:result) { encryption_client['users'].delete_many(ssn: ssn) } - it_behaves_like 'an encrypted command' - end - - describe '#find' do - shared_examples 'it performs an encrypted command' do - include_context 'encrypted document in collection' - - let(:result) { encryption_client['users'].find(ssn: ssn).first } - - it 'encrypts the command and decrypts the response' do - result.should_not be_nil - expect(result['ssn']).to eq(ssn) - end + it 'decrypts the SSN field' do + expect(result.deleted_count).to eq(2) + end - context 'when bypass_auto_encryption=true' do - include_context 'bypass auto encryption' + context 'with bypass_auto_encryption=true' do + include_context 'bypass auto encryption' - it 'does not encrypt the command' do - expect(result).to be_nil + it 'does not encrypt the SSN field' do + expect(result.deleted_count).to eq(0) + end + end end - end - end - - it_behaves_like 'an encrypted command' - end - - describe '#find_one_and_delete' do - shared_examples 'it performs an encrypted command' do - include_context 'encrypted document in collection' - let(:result) { encryption_client['users'].find_one_and_delete(ssn: ssn) } - - it 'encrypts the command and decrypts the response' do - expect(result['ssn']).to eq(ssn) + it_behaves_like 'an encrypted command' end - context 'when bypass_auto_encryption=true' do - include_context 'bypass auto encryption' - - it 'does not encrypt the command' do - expect(result).to be_nil - end - - it 'still decrypts the command' do - result = encryption_client['users'].find_one_and_delete(ssn: encrypted_ssn_binary) - expect(result['ssn']).to eq(ssn) - end - end - end + describe '#find' do + shared_examples 'it performs an encrypted command' do + include_context 'encrypted document in collection' - it_behaves_like 'an encrypted command' - end + let(:result) { encryption_client['users'].find(ssn: ssn).first } - describe '#find_one_and_replace' do - shared_examples 'it performs an encrypted command' do - let(:name) { 'Alan Turing' } + it 'encrypts the command and decrypts the response' do + result.should_not be_nil + expect(result['ssn']).to eq(ssn) + end - context 'with :return_document => :before' do - include_context 'encrypted document in collection' + context 'when bypass_auto_encryption=true' do + include_context 'bypass auto encryption' - let(:result) do - encryption_client['users'].find_one_and_replace( - { ssn: ssn }, - { name: name }, - return_document: :before - ) + it 'does not encrypt the command' do + expect(result).to be_nil + end + end end - it 'encrypts the command and decrypts the response, returning original document' do - expect(result['ssn']).to eq(ssn) - - documents = client['users'].find - expect(documents.count).to eq(1) - expect(documents.first['ssn']).to be_nil - end + it_behaves_like 'an encrypted command' end - context 'with :return_document => :after' do - before do - client['users'].insert_one(name: name) - end + describe '#find_one_and_delete' do + shared_examples 'it performs an encrypted command' do + include_context 'encrypted document in collection' - let(:result) do - encryption_client['users'].find_one_and_replace( - { name: name }, - { ssn: ssn }, - return_document: :after - ) - end + let(:result) { encryption_client['users'].find_one_and_delete(ssn: ssn) } - it 'encrypts the command and decrypts the response, returning new document' do - expect(result['ssn']).to eq(ssn) + it 'encrypts the command and decrypts the response' do + expect(result['ssn']).to eq(ssn) + end - documents = client['users'].find - expect(documents.count).to eq(1) - expect(documents.first['ssn']).to eq(encrypted_ssn_binary) - end - end + context 'when bypass_auto_encryption=true' do + include_context 'bypass auto encryption' - context 'when bypass_auto_encryption=true' do - include_context 'bypass auto encryption' - include_context 'encrypted document in collection' + it 'does not encrypt the command' do + expect(result).to be_nil + end - let(:result) do - encryption_client['users'].find_one_and_replace( - { ssn: encrypted_ssn_binary }, - { name: name }, - :return_document => :before - ) + it 'still decrypts the command' do + result = encryption_client['users'].find_one_and_delete(ssn: encrypted_ssn_binary) + expect(result['ssn']).to eq(ssn) + end + end end - it 'does not encrypt the command but still decrypts the response, returning original document' do - expect(result['ssn']).to eq(ssn) - - documents = client['users'].find - expect(documents.count).to eq(1) - expect(documents.first['ssn']).to be_nil - end + it_behaves_like 'an encrypted command' end - end - - it_behaves_like 'an encrypted command' - end - describe '#find_one_and_update' do - shared_examples 'it performs an encrypted command' do - include_context 'encrypted document in collection' + describe '#find_one_and_replace' do + shared_examples 'it performs an encrypted command' do + let(:name) { 'Alan Turing' } - let(:name) { 'Alan Turing' } + context 'with :return_document => :before' do + include_context 'encrypted document in collection' - let(:result) do - encryption_client['users'].find_one_and_update( - { ssn: ssn }, - { name: name } - ) - end + let(:result) do + encryption_client['users'].find_one_and_replace( + { ssn: ssn }, + { name: name }, + return_document: :before + ) + end - it 'encrypts the command and decrypts the response' do - expect(result['ssn']).to eq(ssn) + it 'encrypts the command and decrypts the response, returning original document' do + expect(result['ssn']).to eq(ssn) - documents = client['users'].find - expect(documents.count).to eq(1) - expect(documents.first['ssn']).to be_nil - end + documents = client['users'].find + expect(documents.count).to eq(1) + expect(documents.first['ssn']).to be_nil + end + end - context 'with bypass_auto_encryption=true' do - include_context 'bypass auto encryption' + context 'with :return_document => :after' do + before do + client['users'].insert_one(name: name) + end + + let(:result) do + encryption_client['users'].find_one_and_replace( + { name: name }, + { ssn: ssn }, + return_document: :after + ) + end + + it 'encrypts the command and decrypts the response, returning new document' do + expect(result['ssn']).to eq(ssn) + + documents = client['users'].find + expect(documents.count).to eq(1) + expect(documents.first['ssn']).to eq(encrypted_ssn_binary) + end + end - it 'does not encrypt the command' do - expect(result).to be_nil + context 'when bypass_auto_encryption=true' do + include_context 'bypass auto encryption' + include_context 'encrypted document in collection' + + let(:result) do + encryption_client['users'].find_one_and_replace( + { ssn: encrypted_ssn_binary }, + { name: name }, + :return_document => :before + ) + end + + it 'does not encrypt the command but still decrypts the response, returning original document' do + expect(result['ssn']).to eq(ssn) + + documents = client['users'].find + expect(documents.count).to eq(1) + expect(documents.first['ssn']).to be_nil + end + end end - it 'still decrypts the response' do - # Query using the encrypted ssn value so the find will succeed - result = encryption_client['users'].find_one_and_update( - { ssn: encrypted_ssn_binary }, - { name: name } - ) - - expect(result['ssn']).to eq(ssn) - end + it_behaves_like 'an encrypted command' end - end - - it_behaves_like 'an encrypted command' - end - describe '#insert_one' do - let(:query) { { ssn: ssn } } - let(:result) { encryption_client['users'].insert_one(query) } + describe '#find_one_and_update' do + shared_examples 'it performs an encrypted command' do + include_context 'encrypted document in collection' - shared_examples 'it performs an encrypted command' do - it 'encrypts the ssn field' do - expect(result).to be_ok - expect(result.inserted_ids.length).to eq(1) + let(:name) { 'Alan Turing' } - id = result.inserted_ids.first - - document = client['users'].find(_id: id).first - document.should_not be_nil - expect(document['ssn']).to eq(encrypted_ssn_binary) - end - end + let(:result) do + encryption_client['users'].find_one_and_update( + { ssn: ssn }, + { name: name } + ) + end - shared_examples 'it obeys bypass_auto_encryption option' do - include_context 'bypass auto encryption' + it 'encrypts the command and decrypts the response' do + expect(result['ssn']).to eq(ssn) - it 'does not encrypt the command' do - result = encryption_client['users'].insert_one(ssn: ssn) - expect(result).to be_ok - expect(result.inserted_ids.length).to eq(1) + documents = client['users'].find + expect(documents.count).to eq(1) + expect(documents.first['ssn']).to be_nil + end - id = result.inserted_ids.first + context 'with bypass_auto_encryption=true' do + include_context 'bypass auto encryption' - document = client['users'].find(_id: id).first - expect(document['ssn']).to eq(ssn) - end - end + it 'does not encrypt the command' do + expect(result).to be_nil + end - it_behaves_like 'an encrypted command' + it 'still decrypts the response' do + # Query using the encrypted ssn value so the find will succeed + result = encryption_client['users'].find_one_and_update( + { ssn: encrypted_ssn_binary }, + { name: name } + ) - context 'with jsonSchema in schema_map option' do - include_context 'schema map in client options' + expect(result['ssn']).to eq(ssn) + end + end + end - context 'with AWS KMS provider' do - include_context 'with AWS kms_providers' - it_behaves_like 'it obeys bypass_auto_encryption option' + it_behaves_like 'an encrypted command' end - context 'with Azure KMS provider' do - include_context 'with Azure kms_providers' - it_behaves_like 'it obeys bypass_auto_encryption option' - end + describe '#insert_one' do + let(:query) { { ssn: ssn } } + let(:result) { encryption_client['users'].insert_one(query) } - context 'with GCP KMS provider' do - include_context 'with GCP kms_providers' - it_behaves_like 'it obeys bypass_auto_encryption option' - end + shared_examples 'it performs an encrypted command' do + it 'encrypts the ssn field' do + expect(result).to be_ok + expect(result.inserted_ids.length).to eq(1) - context 'with KMIP KMS provider' do - include_context 'with KMIP kms_providers' - it_behaves_like 'it obeys bypass_auto_encryption option' - end + id = result.inserted_ids.first + document = client['users'].find(_id: id).first + document.should_not be_nil + expect(document['ssn']).to eq(encrypted_ssn_binary) + end + end - context 'with local KMS provider and ' do - include_context 'with local kms_providers' - it_behaves_like 'it obeys bypass_auto_encryption option' - end - end + shared_examples 'it obeys bypass_auto_encryption option' do + include_context 'bypass auto encryption' - context 'with schema_map client option pointing to wrong collection' do - let(:local_schema) { { 'wrong_db.wrong_coll' => schema_map } } + it 'does not encrypt the command' do + result = encryption_client['users'].insert_one(ssn: ssn) + expect(result).to be_ok + expect(result.inserted_ids.length).to eq(1) - include_context 'with local kms_providers' + id = result.inserted_ids.first - it 'does not raise an exception but doesn\'t encrypt either' do - expect do - result - end.not_to raise_error + document = client['users'].find(_id: id).first + expect(document['ssn']).to eq(ssn) + end + end - expect(result).to be_ok - id = result.inserted_ids.first + it_behaves_like 'an encrypted command' - document = client['users'].find(_id: id).first - document.should_not be_nil - # Document was not encrypted - expect(document['ssn']).to eq(ssn) - end - end + context 'with jsonSchema in schema_map option' do + include_context 'schema map in client options' + + context 'with AWS KMS provider' do + include_context 'with AWS kms_providers' + it_behaves_like 'it obeys bypass_auto_encryption option' + end - context 'encrypting using key alt name' do - include_context 'schema map in client options' + context 'with Azure KMS provider' do + include_context 'with Azure kms_providers' + it_behaves_like 'it obeys bypass_auto_encryption option' + end - let(:query) { { ssn: ssn, altname: key_alt_name } } + context 'with GCP KMS provider' do + include_context 'with GCP kms_providers' + it_behaves_like 'it obeys bypass_auto_encryption option' + end - context 'with AWS KMS provider' do - include_context 'with AWS kms_providers and key alt names' - it 'encrypts the ssn field' do - expect(result).to be_ok - expect(result.inserted_ids.length).to eq(1) + context 'with KMIP KMS provider' do + include_context 'with KMIP kms_providers' + it_behaves_like 'it obeys bypass_auto_encryption option' + end - id = result.inserted_ids.first - document = client['users'].find(_id: id).first - document.should_not be_nil - # Auto-encryption with key alt names only works with random encryption, - # so it will not generate the same result on every test run. - expect(document['ssn']).to be_ciphertext + context 'with local KMS provider and ' do + include_context 'with local kms_providers' + it_behaves_like 'it obeys bypass_auto_encryption option' + end end - end - context 'with Azure KMS provider' do - include_context 'with Azure kms_providers and key alt names' - it 'encrypts the ssn field' do - expect(result).to be_ok - expect(result.inserted_ids.length).to eq(1) + context 'with schema_map client option pointing to wrong collection' do + let(:local_schema) { { 'wrong_db.wrong_coll' => schema_map } } - id = result.inserted_ids.first + include_context 'with local kms_providers' - document = client['users'].find(_id: id).first - document.should_not be_nil - # Auto-encryption with key alt names only works with random encryption, - # so it will not generate the same result on every test run. - expect(document['ssn']).to be_ciphertext - end + it 'does not raise an exception but doesn\'t encrypt either' do + expect do + result + end.not_to raise_error - context 'with GCP KMS provider' do - include_context 'with GCP kms_providers and key alt names' - it 'encrypts the ssn field' do expect(result).to be_ok - expect(result.inserted_ids.length).to eq(1) - id = result.inserted_ids.first document = client['users'].find(_id: id).first document.should_not be_nil - # Auto-encryption with key alt names only works with random encryption, - # so it will not generate the same result on every test run. - expect(document['ssn']).to be_ciphertext + # Document was not encrypted + expect(document['ssn']).to eq(ssn) end end - context 'with KMIP KMS provider' do - include_context 'with KMIP kms_providers and key alt names' - it 'encrypts the ssn field' do - expect(result).to be_ok - expect(result.inserted_ids.length).to eq(1) + context 'encrypting using key alt name' do + include_context 'schema map in client options' - id = result.inserted_ids.first + let(:query) { { ssn: ssn, altname: key_alt_name } } - document = client['users'].find(_id: id).first - document.should_not be_nil - # Auto-encryption with key alt names only works with random encryption, - # so it will not generate the same result on every test run. - expect(document['ssn']).to be_ciphertext + context 'with AWS KMS provider' do + include_context 'with AWS kms_providers and key alt names' + it 'encrypts the ssn field' do + expect(result).to be_ok + expect(result.inserted_ids.length).to eq(1) + + id = result.inserted_ids.first + + document = client['users'].find(_id: id).first + document.should_not be_nil + # Auto-encryption with key alt names only works with random encryption, + # so it will not generate the same result on every test run. + expect(document['ssn']).to be_ciphertext + end + end + + context 'with Azure KMS provider' do + include_context 'with Azure kms_providers and key alt names' + it 'encrypts the ssn field' do + expect(result).to be_ok + expect(result.inserted_ids.length).to eq(1) + + id = result.inserted_ids.first + + document = client['users'].find(_id: id).first + document.should_not be_nil + # Auto-encryption with key alt names only works with random encryption, + # so it will not generate the same result on every test run. + expect(document['ssn']).to be_ciphertext + end + + context 'with GCP KMS provider' do + include_context 'with GCP kms_providers and key alt names' + it 'encrypts the ssn field' do + expect(result).to be_ok + expect(result.inserted_ids.length).to eq(1) + + id = result.inserted_ids.first + + document = client['users'].find(_id: id).first + document.should_not be_nil + # Auto-encryption with key alt names only works with random encryption, + # so it will not generate the same result on every test run. + expect(document['ssn']).to be_ciphertext + end + end + + context 'with KMIP KMS provider' do + include_context 'with KMIP kms_providers and key alt names' + it 'encrypts the ssn field' do + expect(result).to be_ok + expect(result.inserted_ids.length).to eq(1) + + id = result.inserted_ids.first + + document = client['users'].find(_id: id).first + document.should_not be_nil + # Auto-encryption with key alt names only works with random encryption, + # so it will not generate the same result on every test run. + expect(document['ssn']).to be_ciphertext + end + end end - end - end - context 'with local KMS provider' do - include_context 'with local kms_providers and key alt names' - it 'encrypts the ssn field' do - expect(result).to be_ok - expect(result.inserted_ids.length).to eq(1) + context 'with local KMS provider' do + include_context 'with local kms_providers and key alt names' + it 'encrypts the ssn field' do + expect(result).to be_ok + expect(result.inserted_ids.length).to eq(1) - id = result.inserted_ids.first + id = result.inserted_ids.first - document = client['users'].find(_id: id).first - document.should_not be_nil - # Auto-encryption with key alt names only works with random encryption, - # so it will not generate the same result on every test run. - expect(document['ssn']).to be_a_kind_of(BSON::Binary) + document = client['users'].find(_id: id).first + document.should_not be_nil + # Auto-encryption with key alt names only works with random encryption, + # so it will not generate the same result on every test run. + expect(document['ssn']).to be_a_kind_of(BSON::Binary) + end + end end end - end - end - describe '#replace_one' do - shared_examples 'it performs an encrypted command' do - include_context 'encrypted document in collection' + describe '#replace_one' do + shared_examples 'it performs an encrypted command' do + include_context 'encrypted document in collection' - let(:replacement_ssn) { '098-765-4321' } + let(:replacement_ssn) { '098-765-4321' } - let(:result) do - encryption_client['users'].replace_one( - { ssn: ssn }, - { ssn: replacement_ssn } - ) - end + let(:result) do + encryption_client['users'].replace_one( + { ssn: ssn }, + { ssn: replacement_ssn } + ) + end - it 'encrypts the ssn field' do - expect(result.modified_count).to eq(1) + it 'encrypts the ssn field' do + expect(result.modified_count).to eq(1) - find_result = encryption_client['users'].find(ssn: '098-765-4321') - expect(find_result.count).to eq(1) - end + find_result = encryption_client['users'].find(ssn: '098-765-4321') + expect(find_result.count).to eq(1) + end - context 'with bypass_auto_encryption=true' do - include_context 'bypass auto encryption' + context 'with bypass_auto_encryption=true' do + include_context 'bypass auto encryption' - it 'does not encrypt the command' do - expect(result.modified_count).to eq(0) + it 'does not encrypt the command' do + expect(result.modified_count).to eq(0) + end + end end - end - end - it_behaves_like 'an encrypted command' - end + it_behaves_like 'an encrypted command' + end - describe '#update_one' do - shared_examples 'it performs an encrypted command' do - include_context 'encrypted document in collection' + describe '#update_one' do + shared_examples 'it performs an encrypted command' do + include_context 'encrypted document in collection' - let(:result) do - encryption_client['users'].replace_one({ ssn: ssn }, { ssn: '098-765-4321' }) - end + let(:result) do + encryption_client['users'].replace_one({ ssn: ssn }, { ssn: '098-765-4321' }) + end - it 'encrypts the ssn field' do - expect(result.n).to eq(1) + it 'encrypts the ssn field' do + expect(result.n).to eq(1) - find_result = encryption_client['users'].find(ssn: '098-765-4321') - expect(find_result.count).to eq(1) - end + find_result = encryption_client['users'].find(ssn: '098-765-4321') + expect(find_result.count).to eq(1) + end - context 'with bypass_auto_encryption=true' do - include_context 'bypass auto encryption' + context 'with bypass_auto_encryption=true' do + include_context 'bypass auto encryption' - it 'does not encrypt the command' do - expect(result.n).to eq(0) + it 'does not encrypt the command' do + expect(result.n).to eq(0) + end + end end - end - end - - it_behaves_like 'an encrypted command' - end - describe '#update_many' do - shared_examples 'it performs an encrypted command' do - before do - client['users'].insert_one(ssn: encrypted_ssn_binary, age: 25) - client['users'].insert_one(ssn: encrypted_ssn_binary, age: 43) + it_behaves_like 'an encrypted command' end - let(:result) do - encryption_client['users'].update_many({ ssn: ssn }, { "$inc" => { :age => 1 } }) - end + describe '#update_many' do + shared_examples 'it performs an encrypted command' do + before do + client['users'].insert_one(ssn: encrypted_ssn_binary, age: 25) + client['users'].insert_one(ssn: encrypted_ssn_binary, age: 43) + end + + let(:result) do + encryption_client['users'].update_many({ ssn: ssn }, { "$inc" => { :age => 1 } }) + end - it 'encrypts the ssn field' do - expect(result.n).to eq(2) + it 'encrypts the ssn field' do + expect(result.n).to eq(2) - updated_documents = encryption_client['users'].find(ssn: ssn) - ages = updated_documents.map { |doc| doc['age'] } - expect(ages).to include(26) - expect(ages).to include(44) - end + updated_documents = encryption_client['users'].find(ssn: ssn) + ages = updated_documents.map { |doc| doc['age'] } + expect(ages).to include(26) + expect(ages).to include(44) + end - context 'with bypass_auto_encryption=true' do - include_context 'bypass auto encryption' + context 'with bypass_auto_encryption=true' do + include_context 'bypass auto encryption' - it 'does not encrypt the command' do - expect(result.n).to eq(0) + it 'does not encrypt the command' do + expect(result.n).to eq(0) + end + end end + + it_behaves_like 'an encrypted command' end end - - it_behaves_like 'an encrypted command' end end diff --git a/spec/integration/client_side_encryption/on_demand_aws_credentials_spec.rb b/spec/integration/client_side_encryption/on_demand_aws_credentials_spec.rb index 0aa2bd0b2a..93a400d6a8 100644 --- a/spec/integration/client_side_encryption/on_demand_aws_credentials_spec.rb +++ b/spec/integration/client_side_encryption/on_demand_aws_credentials_spec.rb @@ -37,7 +37,7 @@ it 'raises an error' do expect_any_instance_of( Mongo::Auth::Aws::CredentialsRetriever - ).to receive(:credentials).with(no_args).once.and_raise( + ).to receive(:credentials).with(kind_of(Mongo::CsotTimeoutHolder)).once.and_raise( Mongo::Auth::Aws::CredentialsNotFound ) diff --git a/spec/integration/client_side_encryption/range_explicit_encryption_prose_spec.rb b/spec/integration/client_side_encryption/range_explicit_encryption_prose_spec.rb index 1f1792c76e..d0bdbcce0e 100644 --- a/spec/integration/client_side_encryption/range_explicit_encryption_prose_spec.rb +++ b/spec/integration/client_side_encryption/range_explicit_encryption_prose_spec.rb @@ -6,9 +6,7 @@ # be revisited if these tests ever need to be significantly modified. # rubocop:disable RSpec/ExampleLength describe 'Range Explicit Encryption' do - min_server_version '7.0.0-rc0' - # https://jira.mongodb.org/browse/RUBY-3457 - max_server_version '7.99.99' + min_server_version '8.0.0-rc18' require_libmongocrypt include_context 'define shared FLE helpers' @@ -56,7 +54,7 @@ value, { key_id: key1_id, - algorithm: 'RangePreview', + algorithm: 'Range', contention_factor: 0, range_opts: range_opts } @@ -76,8 +74,8 @@ expr, { key_id: key1_id, - algorithm: 'RangePreview', - query_type: 'rangePreview', + algorithm: 'Range', + query_type: 'range', contention_factor: 0, range_opts: range_opts } @@ -100,8 +98,8 @@ expr, { key_id: key1_id, - algorithm: 'RangePreview', - query_type: 'rangePreview', + algorithm: 'Range', + query_type: 'range', contention_factor: 0, range_opts: range_opts } @@ -123,8 +121,8 @@ expr, { key_id: key1_id, - algorithm: 'RangePreview', - query_type: 'rangePreview', + algorithm: 'Range', + query_type: 'range', contention_factor: 0, range_opts: range_opts } @@ -140,8 +138,8 @@ expr, { key_id: key1_id, - algorithm: 'RangePreview', - query_type: 'rangePreview', + algorithm: 'Range', + query_type: 'range', contention_factor: 0, range_opts: range_opts } @@ -163,7 +161,7 @@ value_converter.call(201), { key_id: key1_id, - algorithm: 'RangePreview', + algorithm: 'Range', contention_factor: 0, range_opts: range_opts } @@ -183,7 +181,7 @@ value, { key_id: key1_id, - algorithm: 'RangePreview', + algorithm: 'Range', contention_factor: 0, range_opts: range_opts } @@ -198,7 +196,7 @@ value_converter.call(6), { key_id: key1_id, - algorithm: 'RangePreview', + algorithm: 'Range', contention_factor: 0, range_opts: { min: value_converter.call(0), @@ -244,7 +242,7 @@ insert_payload = client_encryption.encrypt( num, key_id: key1_id, - algorithm: 'RangePreview', + algorithm: 'Range', contention_factor: 0, range_opts: range_opts ) @@ -290,7 +288,7 @@ insert_payload = client_encryption.encrypt( BSON::Int64.new(num), key_id: key1_id, - algorithm: 'RangePreview', + algorithm: 'Range', contention_factor: 0, range_opts: range_opts ) @@ -337,7 +335,7 @@ insert_payload = client_encryption.encrypt( num, key_id: key1_id, - algorithm: 'RangePreview', + algorithm: 'Range', contention_factor: 0, range_opts: range_opts ) @@ -381,7 +379,7 @@ insert_payload = client_encryption.encrypt( num, key_id: key1_id, - algorithm: 'RangePreview', + algorithm: 'Range', contention_factor: 0, range_opts: range_opts ) @@ -427,7 +425,7 @@ insert_payload = client_encryption.encrypt( Time.new(num), key_id: key1_id, - algorithm: 'RangePreview', + algorithm: 'Range', contention_factor: 0, range_opts: range_opts ) @@ -476,7 +474,7 @@ insert_payload = client_encryption.encrypt( BSON::Decimal128.new(num), key_id: key1_id, - algorithm: 'RangePreview', + algorithm: 'Range', contention_factor: 0, range_opts: range_opts ) @@ -522,7 +520,7 @@ insert_payload = client_encryption.encrypt( BSON::Decimal128.new(num), key_id: key1_id, - algorithm: 'RangePreview', + algorithm: 'Range', contention_factor: 0, range_opts: range_opts ) @@ -535,5 +533,51 @@ include_examples 'common cases' end + + describe 'Range Explicit Encryption applies defaults' do + let(:payload_defaults) do + client_encryption.encrypt( + 123, + key_id: key1_id, + algorithm: 'Range', + contention_factor: 0, + range_opts: { + min: 0, + max: 1000 + } + ) + end + + it 'uses libmongocrypt default' do + payload = client_encryption.encrypt( + 123, + key_id: key1_id, + algorithm: 'Range', + contention_factor: 0, + range_opts: { + min: 0, + max: 1000, + sparsity: 2, + trim_factor: 6 + } + ) + expect(payload.to_s.size).to eq(payload_defaults.to_s.size) + end + + it 'accepts trim_factor 0' do + payload = client_encryption.encrypt( + 123, + key_id: key1_id, + algorithm: 'Range', + contention_factor: 0, + range_opts: { + min: 0, + max: 1000, + trim_factor: 0 + } + ) + expect(payload.to_s.size).to eq(payload_defaults.to_s.size) + end + end end # rubocop:enable RSpec/ExampleLength diff --git a/spec/integration/client_side_operations_timeout/encryption_prose_spec.rb b/spec/integration/client_side_operations_timeout/encryption_prose_spec.rb new file mode 100644 index 0000000000..399df4cc04 --- /dev/null +++ b/spec/integration/client_side_operations_timeout/encryption_prose_spec.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'CSOT for encryption' do + require_libmongocrypt + require_no_multi_mongos + min_server_fcv '4.2' + + include_context 'define shared FLE helpers' + include_context 'with local kms_providers' + + let(:subscriber) { Mrss::EventSubscriber.new } + + describe 'mongocryptd' do + before do + Process.spawn( + 'mongocryptd', + '--pidfilepath=bypass-spawning-mongocryptd.pid', '--port=23000', '--idleShutdownTimeoutSecs=60', + %i[ out err ] => '/dev/null' + ) + end + + let(:client) do + Mongo::Client.new('mongodb://localhost:23000/?timeoutMS=1000').tap do |client| + client.subscribe(Mongo::Monitoring::COMMAND, subscriber) + end + end + + let(:ping_command) do + subscriber.started_events.find do |event| + event.command_name == 'ping' + end&.command + end + + after do + client.close + end + + it 'does not set maxTimeMS for commands sent to mongocryptd' do + expect do + client.use('admin').command(ping: 1) + end.to raise_error(Mongo::Error::OperationFailure) + + expect(ping_command).not_to have_key('maxTimeMS') + end + end + + describe 'ClientEncryption' do + let(:key_vault_client) do + ClientRegistry.instance.new_local_client( + SpecConfig.instance.addresses, + SpecConfig.instance.test_options.merge(timeout_ms: 20) + ) + end + + let(:client_encryption) do + Mongo::ClientEncryption.new( + key_vault_client, + key_vault_namespace: key_vault_namespace, + kms_providers: local_kms_providers + ) + end + + describe '#createDataKey' do + before do + authorized_client.use(key_vault_db)[key_vault_coll].drop + authorized_client.use(key_vault_db)[key_vault_coll].create + authorized_client.use(:admin).command({ + configureFailPoint: 'failCommand', + mode: { + times: 1 + }, + data: { + failCommands: [ 'insert' ], + blockConnection: true, + blockTimeMS: 30 + } + }) + end + + after do + authorized_client.use(:admin).command({ + configureFailPoint: 'failCommand', + mode: 'off', + }) + key_vault_client.close + end + + it 'fails with timeout error' do + expect do + client_encryption.create_data_key('local') + end.to raise_error(Mongo::Error::TimeoutError) + end + end + + describe '#encrypt' do + let!(:data_key_id) do + client_encryption.create_data_key('local') + end + + before do + authorized_client.use(:admin).command({ + configureFailPoint: 'failCommand', + mode: { + times: 1 + }, + data: { + failCommands: [ 'find' ], + blockConnection: true, + blockTimeMS: 30 + } + }) + end + + after do + authorized_client.use(:admin).command({ + configureFailPoint: 'failCommand', + mode: 'off', + }) + end + + it 'fails with timeout error' do + expect do + client_encryption.encrypt('hello', key_id: data_key_id, + algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic') + end.to raise_error(Mongo::Error::TimeoutError) + end + end + end +end diff --git a/spec/integration/connection_pool_populator_spec.rb b/spec/integration/connection_pool_populator_spec.rb index 55d22865d2..98230dd05a 100644 --- a/spec/integration/connection_pool_populator_spec.rb +++ b/spec/integration/connection_pool_populator_spec.rb @@ -26,6 +26,8 @@ declare_topology_double + retry_test + let(:app_metadata) do Mongo::Server::AppMetadata.new(options) end diff --git a/spec/integration/cursor_pinning_spec.rb b/spec/integration/cursor_pinning_spec.rb index b20c11d2a8..a5ade03814 100644 --- a/spec/integration/cursor_pinning_spec.rb +++ b/spec/integration/cursor_pinning_spec.rb @@ -52,73 +52,28 @@ context 'lb' do require_topology :load_balanced - # In load-balanced topology, we cannot create new connections to a - # particular service. + # In load-balanced topology, a cursor retains the connection used to create + # it until the cursor is closed. - context 'when no connection is available' do + context 'when connection is available' do + require_multi_mongos - it 'raises ConnectionCheckOutTimeout' do - server.pool.size.should == 0 + let(:client) { authorized_client.with(max_pool_size: 2) } + it 'does not return connection to the pool if cursor not drained' do + expect(server.pool).not_to receive(:check_in) enum = collection.find({}, batch_size: 1).to_enum - # Still zero because we haven't iterated - server.pool.size.should == 0 - + # Get the first element only; cursor is not drained, so there should + # be no check_in of the connection. enum.next - server.pool.size.should == 1 - - # Grab the connection that was used - server.with_connection do - # This requires a new connection, but we cannot make one. - lambda do - enum.next - end.should raise_error(Mongo::Error::ConnectionCheckOutTimeout) - - server.pool.size.should == 1 - end end - end - - context 'when connection is available' do - require_multi_mongos - - let(:client) { authorized_client.with(max_pool_size: 4) } - - it 'uses the available connection' do - server.pool.size.should == 0 - - # Create 4 connections. - - enums = [] - connections = [] - connection_ids = [] - - 4.times do - view = collection.find({}, batch_size: 1) - enum = view.to_enum - - enum.next - - enums << enum - connection_ids << view.cursor.initial_result.connection_global_id - connections << server.pool.check_out - end - - connection_ids.uniq.length.should be > 1 - - server.pool.size.should == 4 - - connections.each do |c| - server.pool.check_in(c) - end - # At this point, in theory, all connections are equally likely to - # be chosen, but we have cursors referencing more than one - # distinct service. - # Iterate each cursor to ensure they all continue to work. - enums.each do |enum| - enum.next - end + it 'returns connection to the pool when cursor is drained' do + view = collection.find({}, batch_size: 1) + enum = view.to_enum + expect_any_instance_of(Mongo::Cursor).to receive(:check_in_connection) + # Drain the cursor + enum.each { |it| it.nil? } end end end diff --git a/spec/integration/cursor_reaping_spec.rb b/spec/integration/cursor_reaping_spec.rb index 876ae46a91..9bd8891378 100644 --- a/spec/integration/cursor_reaping_spec.rb +++ b/spec/integration/cursor_reaping_spec.rb @@ -24,7 +24,7 @@ let(:subscriber) { Mrss::EventSubscriber.new } let(:client) do - authorized_client.tap do |client| + authorized_client.with(max_pool_size: 10).tap do |client| client.subscribe(Mongo::Monitoring::COMMAND, subscriber) end end diff --git a/spec/integration/docs_examples_spec.rb b/spec/integration/docs_examples_spec.rb index bf1ae9e1b0..73a9de6ad6 100644 --- a/spec/integration/docs_examples_spec.rb +++ b/spec/integration/docs_examples_spec.rb @@ -9,7 +9,7 @@ # the tests in this file. begin ClientRegistry.instance.global_client('authorized')['_placeholder'].create - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e # Collection already exists if e.code != 48 raise diff --git a/spec/integration/operation_failure_code_spec.rb b/spec/integration/operation_failure_code_spec.rb index bd124f710d..e777949e6b 100644 --- a/spec/integration/operation_failure_code_spec.rb +++ b/spec/integration/operation_failure_code_spec.rb @@ -17,7 +17,7 @@ collection.insert_one(_id: 1) collection.insert_one(_id: 1) fail('Should have raised') - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e expect(e.code).to eq(11000) # 4.0 and 4.2 sharded clusters set code name. # 4.0 and 4.2 replica sets and standalones do not, diff --git a/spec/integration/operation_failure_message_spec.rb b/spec/integration/operation_failure_message_spec.rb index d865c2d981..ee2ec0600c 100644 --- a/spec/integration/operation_failure_message_spec.rb +++ b/spec/integration/operation_failure_message_spec.rb @@ -22,7 +22,7 @@ begin client.command(bogus_command: nil) fail('Should have raised') - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e e.code_name.should == 'CommandNotFound' e.message.should =~ %r,\A\[59:CommandNotFound\]: no such (?:command|cmd): '?bogus_command'?, end @@ -36,7 +36,7 @@ begin client.command(bogus_command: nil) fail('Should have raised') - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e e.code_name.should be nil e.message.should =~ %r,\A\[59\]: no such (?:command|cmd): '?bogus_command'?, end @@ -53,7 +53,7 @@ collection.insert_one(_id: 1) collection.insert_one(_id: 1) fail('Should have raised') - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e e.code_name.should be nil e.message.should =~ %r,\A\[11000\]: (?:insertDocument :: caused by :: 11000 )?E11000 duplicate key error (?:collection|index):, end diff --git a/spec/integration/retryable_errors_spec.rb b/spec/integration/retryable_errors_spec.rb index ea1aa960a9..1944701461 100644 --- a/spec/integration/retryable_errors_spec.rb +++ b/spec/integration/retryable_errors_spec.rb @@ -83,7 +83,7 @@ begin collection.find(a: 1).to_a - rescue Mongo::Error::OperationFailure => exception + rescue Mongo::Error::OperationFailure::Family => exception else fail('Expected operation to fail') end @@ -128,7 +128,7 @@ begin collection.insert_one(a: 1) - rescue Mongo::Error::OperationFailure => exception + rescue Mongo::Error::OperationFailure::Family => exception else fail('Expected operation to fail') end diff --git a/spec/integration/sdam_error_handling_spec.rb b/spec/integration/sdam_error_handling_spec.rb index fe96a7cc97..c93ea31ae7 100644 --- a/spec/integration/sdam_error_handling_spec.rb +++ b/spec/integration/sdam_error_handling_spec.rb @@ -420,7 +420,8 @@ expect_server_state_change end - it_behaves_like 'marks server unknown and clears connection pool' + # https://jira.mongodb.org/browse/RUBY-2523 + # it_behaves_like 'marks server unknown and clears connection pool' after do admin_client.command(configureFailPoint: 'failCommand', mode: 'off') diff --git a/spec/integration/search_indexes_prose_spec.rb b/spec/integration/search_indexes_prose_spec.rb index 0a17accaf2..75ab4736d7 100644 --- a/spec/integration/search_indexes_prose_spec.rb +++ b/spec/integration/search_indexes_prose_spec.rb @@ -87,6 +87,10 @@ def filter_results(result, names) let(:definition) { { 'mappings' => { 'dynamic' => false } } } let(:create_index) { helper.collection.search_indexes.create_one(definition, name: name) } + after do + client.close + end + # Case 1: Driver can successfully create and list search indexes context 'when creating and listing search indexes' do let(:index) { helper.wait_for(name).first } diff --git a/spec/integration/server_spec.rb b/spec/integration/server_spec.rb index 44be905173..edaf5b8f0b 100644 --- a/spec/integration/server_spec.rb +++ b/spec/integration/server_spec.rb @@ -6,6 +6,7 @@ describe 'Server' do let(:client) { authorized_client } + let(:context) { Mongo::Operation::Context.new(client: client) } let(:server) { client.cluster.next_primary } let(:collection) { client['collection'] } @@ -15,7 +16,7 @@ context 'it performs read operations and receives the correct result type' do context 'normal server' do it 'can be used for reads' do - result = view.send(:send_initial_query, server) + result = view.send(:send_initial_query, server, context) expect(result).to be_a(Mongo::Operation::Find::Result) end end @@ -35,7 +36,7 @@ it 'can be used for reads' do # See also RUBY-3102. - result = view.send(:send_initial_query, server) + result = view.send(:send_initial_query, server, context) expect(result).to be_a(Mongo::Operation::Find::Result) end end @@ -57,7 +58,7 @@ it 'is unusable' do # See also RUBY-3102. lambda do - view.send(:send_initial_query, server) + view.send(:send_initial_query, server, context) end.should raise_error(Mongo::Error::ServerNotUsable) end end diff --git a/spec/integration/transactions_api_examples_spec.rb b/spec/integration/transactions_api_examples_spec.rb index daec64dc99..66302f82e2 100644 --- a/spec/integration/transactions_api_examples_spec.rb +++ b/spec/integration/transactions_api_examples_spec.rb @@ -58,5 +58,7 @@ # End Transactions withTxn API Example 1 + # Do not leak clients. + client.close end end diff --git a/spec/kerberos/kerberos_spec.rb b/spec/kerberos/kerberos_spec.rb index 86d09f50e4..a785a25c11 100644 --- a/spec/kerberos/kerberos_spec.rb +++ b/spec/kerberos/kerberos_spec.rb @@ -20,6 +20,10 @@ def require_env_value(key) end end + after do + client&.close + end + let(:user) do "#{require_env_value('SASL_USER')}%40#{realm}" end diff --git a/spec/lite_spec_helper.rb b/spec/lite_spec_helper.rb index 7f7862296c..486d9c4235 100644 --- a/spec/lite_spec_helper.rb +++ b/spec/lite_spec_helper.rb @@ -22,7 +22,9 @@ !defined?(JRUBY_VERSION) || !f.include?('pool-checkout-minPoolSize-connection-maxConnecting.yml') end AUTH_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/auth/*.yml").sort -CLIENT_SIDE_ENCRYPTION_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/client_side_encryption/*.yml").sort +CLIENT_SIDE_ENCRYPTION_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/client_side_encryption/*.yml").sort.delete_if do |spec| + ![ 1, '1', 'yes', 'true' ].include?(ENV['CSOT_SPEC_TESTS']) && spec =~ /.*timeoutMS.yml$/ +end # Disable output buffering: https://www.rubyguides.com/2019/02/ruby-io/ STDOUT.sync = true diff --git a/spec/mongo/auth/user/view_spec.rb b/spec/mongo/auth/user/view_spec.rb index 4986754855..73e620b0ac 100644 --- a/spec/mongo/auth/user/view_spec.rb +++ b/spec/mongo/auth/user/view_spec.rb @@ -526,7 +526,7 @@ it "raises and reports the write concern error correctly" do begin view.send(method, input) - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e expect(e.write_concern_error?).to be true expect(e.write_concern_error_document).to eq( "code" => 64, diff --git a/spec/mongo/caching_cursor_spec.rb b/spec/mongo/caching_cursor_spec.rb index e5b55fe910..e8648f5c10 100644 --- a/spec/mongo/caching_cursor_spec.rb +++ b/spec/mongo/caching_cursor_spec.rb @@ -23,7 +23,7 @@ end let(:reply) do - view.send(:send_initial_query, server) + view.send(:send_initial_query, server, Mongo::Operation::Context.new(client: authorized_client)) end let(:cursor) do diff --git a/spec/mongo/client_encryption_spec.rb b/spec/mongo/client_encryption_spec.rb index ca0e6cd99b..84b3cd59a5 100644 --- a/spec/mongo/client_encryption_spec.rb +++ b/spec/mongo/client_encryption_spec.rb @@ -303,6 +303,7 @@ end it 'raises a KmsError' do + skip 'https://jira.mongodb.org/browse/RUBY-3375' expect do data_key_id end.to raise_error(Mongo::Error::KmsError, /Error while connecting to socket/) diff --git a/spec/mongo/client_spec.rb b/spec/mongo/client_spec.rb index ac8bcc9148..6f37e708e1 100644 --- a/spec/mongo/client_spec.rb +++ b/spec/mongo/client_spec.rb @@ -561,6 +561,75 @@ expect(command['comment']).to eq('comment') end end + + context 'with timeout_ms' do + # To make it easier with failCommand + require_topology :single + min_server_version '4.4' + + before do + root_authorized_client.use('admin').command({ + configureFailPoint: "failCommand", + mode: "alwaysOn", + data: { + failCommands: ["listDatabases"], + blockConnection: true, + blockTimeMS: 100 + } + }) + end + + after do + root_authorized_client.use('admin').command({ + configureFailPoint: "failCommand", + mode: "off" + }) + end + + context 'when timeout_ms is set on command level' do + context 'when there is not enough time' do + it 'raises' do + expect do + monitored_client.database_names({}, timeout_ms: 50) + end.to raise_error(Mongo::Error::TimeoutError) + end + end + + context 'when there is enough time' do + it 'does not raise' do + expect do + monitored_client.database_names({}, timeout_ms: 200) + end.not_to raise_error + end + end + end + + context 'when timeout_ms is set on client level' do + context 'when there is not enough time' do + let(:client) do + root_authorized_client.with(timeout_ms: 50) + end + + it 'raises' do + expect do + client.database_names({}) + end.to raise_error(Mongo::Error::TimeoutError) + end + end + + context 'when there is enough time' do + let(:client) do + root_authorized_client.with(timeout_ms: 200) + end + + it 'does not raise' do + expect do + monitored_client.database_names({}) + end.not_to raise_error + end + end + end + end end describe '#list_databases' do @@ -572,8 +641,6 @@ end context 'when filter criteria is present' do - min_server_fcv '3.6' - include_context 'ensure test db exists' let(:result) do @@ -591,8 +658,6 @@ end context 'when name_only is true' do - min_server_fcv '3.6' - let(:command) do Utils.get_command_event(root_authorized_client, 'listDatabases') do |client| client.list_databases({}, true) @@ -667,6 +732,75 @@ expect(command['comment']).to eq('comment') end end + + context 'with timeout_ms' do + # To make it easier with failCommand + require_topology :single + min_server_version '4.4' + + before do + root_authorized_client.use('admin').command({ + configureFailPoint: "failCommand", + mode: "alwaysOn", + data: { + failCommands: ["listDatabases"], + blockConnection: true, + blockTimeMS: 100 + } + }) + end + + after do + root_authorized_client.use('admin').command({ + configureFailPoint: "failCommand", + mode: "off" + }) + end + + context 'when timeout_ms is set on command level' do + context 'when there is not enough time' do + it 'raises' do + expect do + monitored_client.list_databases({}, false, timeout_ms: 50) + end.to raise_error(Mongo::Error::TimeoutError) + end + end + + context 'when there is enough time' do + it 'does not raise' do + expect do + monitored_client.list_databases({}, false, timeout_ms: 200) + end.not_to raise_error + end + end + end + + context 'when timeout_ms is set on client level' do + context 'when there is not enough time' do + let(:client) do + root_authorized_client.with(timeout_ms: 50) + end + + it 'raises' do + expect do + client.list_databases({}) + end.to raise_error(Mongo::Error::TimeoutError) + end + end + + context 'when there is enough time' do + let(:client) do + root_authorized_client.with(timeout_ms: 200) + end + + it 'does not raise' do + expect do + monitored_client.list_databases({}) + end.not_to raise_error + end + end + end + end end describe '#list_mongo_databases' do @@ -1156,6 +1290,26 @@ }.to raise_exception(Mongo::Error::InvalidSession) end end + + context 'when CSOT is set on the client' do + require_topology :replica_set + + let(:timeout_ms) { 10 } + + let(:timeout_sec) { timeout_ms / 1_000.0 } + + let(:client) do + authorized_client.with(timeout_ms: timeout_ms) + end + + it 'uses CSOT timeout set on the client' do + expect_any_instance_of(Mongo::ServerSelector::PrimaryPreferred).to( + receive(:select_server).with(anything, {timeout: timeout_sec}).and_call_original + ) + + client.start_session + end + end end describe '#summary' do diff --git a/spec/mongo/collection/view/aggregation_spec.rb b/spec/mongo/collection/view/aggregation_spec.rb index da363d9992..894d1cfcc0 100644 --- a/spec/mongo/collection/view/aggregation_spec.rb +++ b/spec/mongo/collection/view/aggregation_spec.rb @@ -156,8 +156,6 @@ end context 'when the initial response has no results but an active cursor' do - min_server_fcv '3.2' - let(:documents) do [ { city: 'a'*6000000 }, @@ -166,7 +164,7 @@ end let(:options) do - { :use_cursor => true } + {} end let(:pipeline) do @@ -486,48 +484,25 @@ end end - context 'when use_cursor is set' do - - context 'when use_cursor is true' do - - context 'when batch_size is set' do - - let(:options) do - { :use_cursor => true, - :batch_size => 10 - } - end - - it 'sets a batch size document in the spec' do - expect(aggregation_spec[:selector][:cursor][:batchSize]).to eq(options[:batch_size]) - end - end - - context 'when batch_size is not set' do - - let(:options) do - { :use_cursor => true } - end + context 'when batch_size is set' do - it 'sets an empty document in the spec' do - expect(aggregation_spec[:selector][:cursor]).to eq({}) - end - end + let(:options) do + { :batch_size => 10 } + end + it 'sets a batch size document in the spec' do + expect(aggregation_spec[:selector][:cursor][:batchSize]).to eq(options[:batch_size]) end + end - context 'when use_cursor is false' do + context 'when batch_size is not set' do - let(:options) do - { :use_cursor => false } - end - - context 'when batch_size is set' do + let(:options) do + {} + end - it 'does not set the cursor option in the spec' do - expect(aggregation_spec[:selector][:cursor]).to be_nil - end - end + it 'sets an empty document in the spec' do + expect(aggregation_spec[:selector][:cursor]).to eq({}) end end end diff --git a/spec/mongo/collection/view/change_stream_spec.rb b/spec/mongo/collection/view/change_stream_spec.rb index ad25ad65a8..42446d2eeb 100644 --- a/spec/mongo/collection/view/change_stream_spec.rb +++ b/spec/mongo/collection/view/change_stream_spec.rb @@ -507,7 +507,7 @@ end it 'includes the max_await_time value in the formatted string' do - expect(change_stream.inspect).to include({ max_await_time_ms: 10 }.to_s) + expect(change_stream.inspect).to include({ 'max_await_time_ms' => 10 }.to_s) end end @@ -518,7 +518,7 @@ end it 'includes the batch_size value in the formatted string' do - expect(change_stream.inspect).to include({ batch_size: 5 }.to_s) + expect(change_stream.inspect).to include({ 'batch_size' => 5 }.to_s) end end @@ -529,7 +529,7 @@ end it 'includes the collation value in the formatted string' do - expect(change_stream.inspect).to include({ 'collation' => { locale: 'en_US', strength: 2 } }.to_s) + expect(change_stream.inspect).to include({ 'collation' => { 'locale' => 'en_US', 'strength' => 2 } }.to_s) end end diff --git a/spec/mongo/collection_spec.rb b/spec/mongo/collection_spec.rb index ef58e44c84..a6d37224d1 100644 --- a/spec/mongo/collection_spec.rb +++ b/spec/mongo/collection_spec.rb @@ -828,13 +828,12 @@ let(:enum) { change_stream.to_enum } + let(:get_more) { subscriber.started_events.detect { |e| e.command['getMore'] }.command } + it 'sets the option correctly' do - expect(change_stream.instance_variable_get(:@cursor)).to receive(:get_more_operation).once.and_wrap_original do |m, *args, &block| - m.call(*args).tap do |op| - expect(op.max_time_ms).to eq(3000) - end - end - enum.next + enum.try_next + expect(get_more).not_to be_nil + expect(get_more['maxTimeMS']).to be == 3000 end it "waits the appropriate amount of time" do diff --git a/spec/mongo/crypt/auto_encrypter_spec.rb b/spec/mongo/crypt/auto_encrypter_spec.rb index 0c9aeb7922..5ed3246577 100644 --- a/spec/mongo/crypt/auto_encrypter_spec.rb +++ b/spec/mongo/crypt/auto_encrypter_spec.rb @@ -58,6 +58,8 @@ ) end + let(:operation_context) { Mongo::Operation::Context.new } + shared_context 'with jsonSchema validator' do before do users_collection = client.use(db_name)[collection_name] @@ -81,14 +83,14 @@ shared_examples 'a functioning auto encrypter' do describe '#encrypt' do it 'replaces the ssn field with a BSON::Binary' do - result = auto_encrypter.encrypt(db_name, command) + result = auto_encrypter.encrypt(db_name, command, operation_context) expect(result).to eq(encrypted_command) end end describe '#decrypt' do it 'returns the unencrypted document' do - result = auto_encrypter.decrypt(encrypted_command) + result = auto_encrypter.decrypt(encrypted_command, operation_context) expect(result).to eq(command) end end @@ -329,14 +331,14 @@ describe '#encrypt' do it 'does not perform encryption' do - result = auto_encrypter.encrypt(db_name, command) + result = auto_encrypter.encrypt(db_name, command, operation_context) expect(result).to eq(command) end end describe '#decrypt' do it 'still performs decryption' do - result = auto_encrypter.decrypt(encrypted_command) + result = auto_encrypter.decrypt(encrypted_command, operation_context) expect(result).to eq(command) end end @@ -347,14 +349,14 @@ describe '#encrypt' do it 'does not perform encryption' do - result = auto_encrypter.encrypt(db_name, command) + result = auto_encrypter.encrypt(db_name, command, operation_context) expect(result).to eq(command) end end describe '#decrypt' do it 'still performs decryption' do - result = auto_encrypter.decrypt(encrypted_command) + result = auto_encrypter.decrypt(encrypted_command, operation_context) expect(result).to eq(command) end end @@ -365,14 +367,14 @@ describe '#encrypt' do it 'does not perform encryption' do - result = auto_encrypter.encrypt(db_name, command) + result = auto_encrypter.encrypt(db_name, command, operation_context) expect(result).to eq(command) end end describe '#decrypt' do it 'still performs decryption' do - result = auto_encrypter.decrypt(encrypted_command) + result = auto_encrypter.decrypt(encrypted_command, operation_context) expect(result).to eq(command) end end @@ -383,14 +385,14 @@ describe '#encrypt' do it 'does not perform encryption' do - result = auto_encrypter.encrypt(db_name, command) + result = auto_encrypter.encrypt(db_name, command, operation_context) expect(result).to eq(command) end end describe '#decrypt' do it 'still performs decryption' do - result = auto_encrypter.decrypt(encrypted_command) + result = auto_encrypter.decrypt(encrypted_command, operation_context) expect(result).to eq(command) end end @@ -401,14 +403,14 @@ describe '#encrypt' do it 'does not perform encryption' do - result = auto_encrypter.encrypt(db_name, command) + result = auto_encrypter.encrypt(db_name, command, operation_context) expect(result).to eq(command) end end describe '#decrypt' do it 'still performs decryption' do - result = auto_encrypter.decrypt(encrypted_command) + result = auto_encrypter.decrypt(encrypted_command, operation_context) expect(result).to eq(command) end end diff --git a/spec/mongo/crypt/data_key_context_spec.rb b/spec/mongo/crypt/data_key_context_spec.rb index babcfac43c..b1b261e07b 100644 --- a/spec/mongo/crypt/data_key_context_spec.rb +++ b/spec/mongo/crypt/data_key_context_spec.rb @@ -136,8 +136,10 @@ ) end + let(:operation_context) { Mongo::Operation::Context.new } + it 'creates a data key' do - expect(context.run_state_machine).to be_a_kind_of(Hash) + expect(context.run_state_machine(operation_context)).to be_a_kind_of(Hash) end end end diff --git a/spec/mongo/crypt/explicit_encryption_context_spec.rb b/spec/mongo/crypt/explicit_encryption_context_spec.rb index c19d0fa715..74350ed87a 100644 --- a/spec/mongo/crypt/explicit_encryption_context_spec.rb +++ b/spec/mongo/crypt/explicit_encryption_context_spec.rb @@ -139,7 +139,7 @@ value, options.merge(query_type: "equality") ) - end.to raise_error(ArgumentError, /query_type is allowed only for "Indexed" or "RangePreview" algorithm/) + end.to raise_error(ArgumentError, /query_type is allowed only for "Indexed" or "Range" algorithm/) end end @@ -154,7 +154,7 @@ value, options.merge(contention_factor: 10) ) - end.to raise_error(ArgumentError, /contention_factor is allowed only for "Indexed" or "RangePreview" algorithm/) + end.to raise_error(ArgumentError, /contention_factor is allowed only for "Indexed" or "Range" algorithm/) end end diff --git a/spec/mongo/crypt/handle_spec.rb b/spec/mongo/crypt/handle_spec.rb index a7af8e3c83..1e4b10006c 100644 --- a/spec/mongo/crypt/handle_spec.rb +++ b/spec/mongo/crypt/handle_spec.rb @@ -188,7 +188,7 @@ end it 'raises an exception' do - expect { handle }.to raise_error(Mongo::Error::CryptError, 'local key must be 96 bytes (libmongocrypt error code 1)') + expect { handle }.to raise_error(Mongo::Error::CryptError, /local key must be 96 bytes \(libmongocrypt error code 1\)/) end end diff --git a/spec/mongo/cursor_spec.rb b/spec/mongo/cursor_spec.rb index bab8c632f6..9aac3655b6 100644 --- a/spec/mongo/cursor_spec.rb +++ b/spec/mongo/cursor_spec.rb @@ -8,6 +8,10 @@ authorized_client['cursor_spec_collection'] end + let(:context) do + Mongo::Operation::Context.new(client: authorized_client) + end + before do authorized_collection.drop end @@ -18,7 +22,7 @@ end let(:reply) do - view.send(:send_initial_query, server) + view.send(:send_initial_query, server, context) end let(:cursor) do @@ -118,7 +122,7 @@ end let(:reply) do - view.send(:send_initial_query, server) + view.send(:send_initial_query, server, context) end let(:cursor) do @@ -170,7 +174,11 @@ before do expect(cursor).to receive(:get_more_operation).and_return(op).ordered - expect(op).to receive(:execute).and_raise(Mongo::Error::SocketError).ordered + if SpecConfig.instance.connect_options[:connect] == :load_balanced + expect(op).to receive(:execute_with_connection).and_raise(Mongo::Error::SocketError).ordered + else + expect(op).to receive(:execute).and_raise(Mongo::Error::SocketError).ordered + end end it 'raises the error' do @@ -617,6 +625,9 @@ allow(reply).to receive(:connection_description).and_return(conn_desc) allow(reply).to receive(:cursor_id).and_return(42) allow(reply).to receive(:connection_global_id).and_return(1) + if SpecConfig.instance.connect_options[:connect] == :load_balanced + allow(reply).to receive(:connection).and_return(nil) + end end end @@ -645,7 +656,7 @@ end let(:reply) do - view.send(:send_initial_query, authorized_primary) + view.send(:send_initial_query, authorized_primary, context) end let(:cursor) do @@ -721,7 +732,7 @@ end let(:reply) do - view.send(:send_initial_query, server) + view.send(:send_initial_query, server, context) end let(:cursor) do @@ -770,10 +781,16 @@ it 'does not raise an error' do cursor - server.with_connection do |conn| - expect(conn).to receive(:deliver) - .at_least(:once) - .and_raise(Mongo::Error::SocketError, "test error") + if SpecConfig.instance.connect_options[:connect] == :load_balanced + expect(cursor.connection).to receive(:deliver) + .at_least(:once) + .and_raise(Mongo::Error::SocketError, "test error") + else + server.with_connection do |conn| + expect(conn).to receive(:deliver) + .at_least(:once) + .and_raise(Mongo::Error::SocketError, "test error") + end end expect do cursor.close diff --git a/spec/mongo/error/operation_failure_heavy_spec.rb b/spec/mongo/error/operation_failure_heavy_spec.rb index e18373992e..b0b41f9126 100644 --- a/spec/mongo/error/operation_failure_heavy_spec.rb +++ b/spec/mongo/error/operation_failure_heavy_spec.rb @@ -39,7 +39,7 @@ begin authorized_client['foo'].insert_one(test: 1) - rescue Mongo::Error::OperationFailure => exc + rescue Mongo::Error::OperationFailure::Family => exc expect(exc.details).to eq(exc.document['writeConcernError']['errInfo']) expect(exc.server_message).to eq(exc.document['writeConcernError']['errmsg']) expect(exc.code).to eq(exc.document['writeConcernError']['code']) @@ -90,7 +90,7 @@ it 'succeeds and prints the error' do begin collection.insert_one({x: 1}) - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e insert_events = subscriber.succeeded_events.select { |e| e.command_name == "insert" } expect(insert_events.length).to eq 1 expect(e.message).to match(/\[#{e.code}(:.*)?\].+ -- .+/) diff --git a/spec/mongo/operation/context_spec.rb b/spec/mongo/operation/context_spec.rb new file mode 100644 index 0000000000..3f2bb1daa9 --- /dev/null +++ b/spec/mongo/operation/context_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'lite_spec_helper' + +describe Mongo::Operation::Context do + describe '#initialize' do + context 'when timeout_ms is negative' do + it 'raises an error' do + expect do + described_class.new(operation_timeouts: { operation_timeout_ms: -1 }) + end.to raise_error ArgumentError, /must be a non-negative integer/ + end + end + end + + describe '#deadline' do + let(:context) { described_class.new(operation_timeouts: { operation_timeout_ms: timeout_ms }) } + + context 'when timeout_ms is nil' do + let(:timeout_ms) { nil } + + it 'returns nil' do + expect(context.deadline).to be_nil + end + end + + context 'when timeout_ms is zero' do + let(:timeout_ms) { 0 } + + it 'returns nil' do + expect(context.deadline).to eq(0) + end + end + + context 'when timeout_ms is positive' do + before do + allow(Mongo::Utils).to receive(:monotonic_time).and_return(100.0) + end + + let(:timeout_ms) { 10_000 } + + it 'calculates the deadline' do + expect(context.deadline).to eq(110) + end + end + end + + describe '#remaining_timeout_ms' do + let(:context) { described_class.new(operation_timeouts: { operation_timeout_ms: timeout_ms }) } + + context 'when timeout_ms is nil' do + let(:timeout_ms) { nil } + + it 'returns nil' do + expect(context.remaining_timeout_ms).to be_nil + end + end + + context 'when timeout_ms is zero' do + let(:timeout_ms) { 0 } + + it 'returns nil' do + expect(context.remaining_timeout_ms).to be_nil + end + end + + context 'when timeout_ms is positive' do + before do + allow(Mongo::Utils).to receive(:monotonic_time).and_return(100.0, 105.0) + end + + let(:timeout_ms) { 10_000 } + + it 'calculates the remaining time' do + expect(context.remaining_timeout_ms).to eq(5_000) + end + end + end +end diff --git a/spec/mongo/operation/create/op_msg_spec.rb b/spec/mongo/operation/create/op_msg_spec.rb index f4033611b8..0ecafc8d52 100644 --- a/spec/mongo/operation/create/op_msg_spec.rb +++ b/spec/mongo/operation/create/op_msg_spec.rb @@ -2,8 +2,12 @@ # rubocop:todo all require 'spec_helper' +require_relative '../shared/csot/examples' describe Mongo::Operation::Create::OpMsg do + include CSOT::Examples + + let(:context) { Mongo::Operation::Context.new } let(:write_concern) do Mongo::WriteConcern.get(w: :majority) @@ -73,8 +77,6 @@ end describe '#selector' do - min_server_fcv '3.6' - it 'does not mutate user input' do user_input = IceNine.deep_freeze(spec.dup) expect do @@ -87,158 +89,152 @@ # https://jira.mongodb.org/browse/RUBY-2224 require_no_linting - context 'when the server supports OP_MSG' do + let(:global_args) do + { + create: TEST_COLL, + writeConcern: write_concern.options, + '$db' => SpecConfig.instance.test_db, + lsid: session.session_id + } + end + + let(:session) do + authorized_client.start_session + end - let(:global_args) do - { - create: TEST_COLL, - writeConcern: write_concern.options, - '$db' => SpecConfig.instance.test_db, - lsid: session.session_id - } + context 'when the topology is replica set or sharded' do + require_topology :replica_set, :sharded + + let(:expected_global_args) do + global_args.merge(Mongo::Operation::CLUSTER_TIME => authorized_client.cluster.cluster_time) end - let(:session) do - authorized_client.start_session + it 'creates the correct OP_MSG message' do + authorized_client.command(ping:1) + expect(Mongo::Protocol::Msg).to receive(:new).with([], {}, expected_global_args) + op.send(:message, connection) end + end - context 'when the topology is replica set or sharded' do - min_server_fcv '3.6' - require_topology :replica_set, :sharded + context 'when the topology is standalone' do + require_topology :single - let(:expected_global_args) do - global_args.merge(Mongo::Operation::CLUSTER_TIME => authorized_client.cluster.cluster_time) - end + let(:expected_global_args) do + global_args + end - it 'creates the correct OP_MSG message' do - authorized_client.command(ping:1) - expect(Mongo::Protocol::Msg).to receive(:new).with([], {}, expected_global_args) - op.send(:message, connection) - end + it 'creates the correct OP_MSG message' do + authorized_client.command(ping:1) + expect(Mongo::Protocol::Msg).to receive(:new).with([], {}, expected_global_args) + op.send(:message, connection) end - context 'when the topology is standalone' do - min_server_fcv '3.6' - require_topology :single + context 'when an implicit session is created and the topology is then updated and the server does not support sessions' do + # Mocks on features are incompatible with linting + require_no_linting let(:expected_global_args) do - global_args - end - - it 'creates the correct OP_MSG message' do - authorized_client.command(ping:1) - expect(Mongo::Protocol::Msg).to receive(:new).with([], {}, expected_global_args) - op.send(:message, connection) - end - - context 'when an implicit session is created and the topology is then updated and the server does not support sessions' do - # Mocks on features are incompatible with linting - require_no_linting - - let(:expected_global_args) do - global_args.dup.tap do |args| - args.delete(:lsid) - end + global_args.dup.tap do |args| + args.delete(:lsid) end + end - let(:session) do - Mongo::Session.new(nil, authorized_client, implicit: true).tap do |session| - allow(session).to receive(:session_id).and_return(42) - session.should be_implicit - end + let(:session) do + Mongo::Session.new(nil, authorized_client, implicit: true).tap do |session| + allow(session).to receive(:session_id).and_return(42) + session.should be_implicit end + end - it 'creates the correct OP_MSG message' do - RSpec::Mocks.with_temporary_scope do - expect(connection.features).to receive(:sessions_enabled?).and_return(false) + it 'creates the correct OP_MSG message' do + RSpec::Mocks.with_temporary_scope do + expect(connection.features).to receive(:sessions_enabled?).and_return(false) - expect(expected_global_args[:session]).to be nil - expect(Mongo::Protocol::Msg).to receive(:new).with([], {}, expected_global_args) - op.send(:message, connection) - end + expect(expected_global_args[:session]).to be nil + expect(Mongo::Protocol::Msg).to receive(:new).with([], {}, expected_global_args) + op.send(:message, connection) end end end + end - context 'when the write concern is 0' do + context 'when the write concern is 0' do - let(:write_concern) do - Mongo::WriteConcern.get(w: 0) - end + let(:write_concern) do + Mongo::WriteConcern.get(w: 0) + end - context 'when the session is implicit' do + context 'when the session is implicit' do - let(:session) do - Mongo::Session.new(nil, authorized_client, implicit: true).tap do |session| - allow(session).to receive(:session_id).and_return(42) - session.should be_implicit - end + let(:session) do + Mongo::Session.new(nil, authorized_client, implicit: true).tap do |session| + allow(session).to receive(:session_id).and_return(42) + session.should be_implicit end + end - context 'when the topology is replica set or sharded' do - min_server_fcv '3.6' - require_topology :replica_set, :sharded + context 'when the topology is replica set or sharded' do + require_topology :replica_set, :sharded - let(:expected_global_args) do - global_args.dup.tap do |args| - args.delete(:lsid) - args.merge!(Mongo::Operation::CLUSTER_TIME => authorized_client.cluster.cluster_time) - end + let(:expected_global_args) do + global_args.dup.tap do |args| + args.delete(:lsid) + args.merge!(Mongo::Operation::CLUSTER_TIME => authorized_client.cluster.cluster_time) end + end - it 'does not send a session id in the command' do - authorized_client.command(ping:1) - expect(Mongo::Protocol::Msg).to receive(:new).with([:more_to_come], {}, expected_global_args) - op.send(:message, connection) - end + it 'does not send a session id in the command' do + authorized_client.command(ping:1) + expect(Mongo::Protocol::Msg).to receive(:new).with([:more_to_come], {}, expected_global_args) + op.send(:message, connection) end + end - context 'when the topology is standalone' do - min_server_fcv '3.6' - require_topology :single + context 'when the topology is standalone' do + require_topology :single - let(:expected_global_args) do - global_args.dup.tap do |args| - args.delete(:lsid) - end + let(:expected_global_args) do + global_args.dup.tap do |args| + args.delete(:lsid) end + end - it 'creates the correct OP_MSG message' do - authorized_client.command(ping:1) - expect(Mongo::Protocol::Msg).to receive(:new).with([:more_to_come], {}, expected_global_args) - op.send(:message, connection) - end + it 'creates the correct OP_MSG message' do + authorized_client.command(ping:1) + expect(Mongo::Protocol::Msg).to receive(:new).with([:more_to_come], {}, expected_global_args) + op.send(:message, connection) end end + end - context 'when the session is explicit' do - min_server_fcv '3.6' - require_topology :replica_set, :sharded + context 'when the session is explicit' do + require_topology :replica_set, :sharded - let(:session) do - authorized_client.start_session - end + let(:session) do + authorized_client.start_session + end - before do - session.should_not be_implicit - end + before do + session.should_not be_implicit + end - let(:expected_global_args) do - global_args.dup.tap do |args| - args.delete(:lsid) - args.merge!(Mongo::Operation::CLUSTER_TIME => authorized_client.cluster.cluster_time) - end + let(:expected_global_args) do + global_args.dup.tap do |args| + args.delete(:lsid) + args.merge!(Mongo::Operation::CLUSTER_TIME => authorized_client.cluster.cluster_time) end + end - it 'does not send a session id in the command' do - authorized_client.command(ping:1) - RSpec::Mocks.with_temporary_scope do - expect(Mongo::Protocol::Msg).to receive(:new).with([:more_to_come], {}, expected_global_args) - op.send(:message, connection) - end + it 'does not send a session id in the command' do + authorized_client.command(ping:1) + RSpec::Mocks.with_temporary_scope do + expect(Mongo::Protocol::Msg).to receive(:new).with([:more_to_come], {}, expected_global_args) + op.send(:message, connection) end end end end end + + it_behaves_like 'a CSOT-compliant OpMsg subclass' end diff --git a/spec/mongo/operation/delete/op_msg_spec.rb b/spec/mongo/operation/delete/op_msg_spec.rb index ab163e3840..2e477df33d 100644 --- a/spec/mongo/operation/delete/op_msg_spec.rb +++ b/spec/mongo/operation/delete/op_msg_spec.rb @@ -2,8 +2,12 @@ # rubocop:todo all require 'spec_helper' +require_relative '../shared/csot/examples' describe Mongo::Operation::Delete::OpMsg do + include CSOT::Examples + + let(:context) { Mongo::Operation::Context.new } let(:write_concern) do Mongo::WriteConcern.get(w: :majority) @@ -125,7 +129,6 @@ end context 'when the topology is replica set or sharded' do - min_server_fcv '3.6' require_topology :replica_set, :sharded let(:expected_global_args) do @@ -140,7 +143,6 @@ end context 'when the topology is standalone' do - min_server_fcv '3.6' require_topology :single let(:expected_global_args) do @@ -198,7 +200,6 @@ end context 'when the topology is replica set or sharded' do - min_server_fcv '3.6' require_topology :replica_set, :sharded let(:expected_global_args) do @@ -216,7 +217,6 @@ end context 'when the topology is standalone' do - min_server_fcv '3.6' require_topology :single let(:expected_global_args) do @@ -234,7 +234,6 @@ end context 'when the session is explicit' do - min_server_fcv '3.6' require_topology :replica_set, :sharded let(:session) do @@ -263,4 +262,6 @@ end end end + + it_behaves_like 'a CSOT-compliant OpMsg subclass' end diff --git a/spec/mongo/operation/find/op_msg_spec.rb b/spec/mongo/operation/find/op_msg_spec.rb new file mode 100644 index 0000000000..5a5868c475 --- /dev/null +++ b/spec/mongo/operation/find/op_msg_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_relative '../shared/csot/examples' + +describe Mongo::Operation::Find::OpMsg do + include CSOT::Examples + + let(:spec) do + { coll_name: 'coll_name', + filter: {}, + db_name: 'db_name' } + end + + let(:op) { described_class.new(spec) } + + context 'when it is a CSOT-compliant OpMsg' do + include_examples 'mock CSOT environment' + + context 'when no timeout_ms set' do + it 'does not set maxTimeMS' do + expect(body.key?(:maxTimeMS)).to be false + end + end + + context 'when timeout_ms is set' do + let(:remaining_timeout_sec) { 3 } + + context 'when cursor is non-tailable' do + let(:cursor_type) { nil } + + context 'when timeout_mode is cursor_lifetime' do + let(:timeout_mode) { :cursor_lifetime } + + it 'sets maxTimeMS' do + expect(body[:maxTimeMS]).to be == 3_000 + end + end + + context 'when timeout_mode is iteration' do + let(:timeout_mode) { :iteration } + + it 'omits maxTimeMS' do + expect(body[:maxTimeMS]).to be_nil + end + end + end + + context 'when cursor is tailable' do + let(:cursor_type) { :tailable } + + it 'omits maxTimeMS' do + expect(body[:maxTimeMS]).to be_nil + end + end + + context 'when cursor is tailable_await' do + let(:cursor_type) { :tailable_await } + + it 'sets maxTimeMS' do + expect(body[:maxTimeMS]).to be == 3_000 + end + end + end + end +end diff --git a/spec/mongo/operation/get_more/op_msg_spec.rb b/spec/mongo/operation/get_more/op_msg_spec.rb new file mode 100644 index 0000000000..408ac6cac6 --- /dev/null +++ b/spec/mongo/operation/get_more/op_msg_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_relative '../shared/csot/examples' + +describe Mongo::Operation::GetMore::OpMsg do + include CSOT::Examples + + let(:spec) do + { + options: {}, + db_name: 'db_name', + coll_name: 'coll_name', + cursor_id: 1_234_567_890, + } + end + + let(:op) { described_class.new(spec) } + + context 'when it is a CSOT-compliant OpMsg' do + include_examples 'mock CSOT environment' + + context 'when no timeout_ms set' do + it 'does not set maxTimeMS' do + expect(body.key?(:maxTimeMS)).to be false + end + end + + context 'when timeout_ms is set' do + let(:remaining_timeout_sec) { 3 } + + context 'when cursor is non-tailable' do + it 'omits maxTimeMS' do + expect(body[:maxTimeMS]).to be_nil + end + end + + context 'when cursor is tailable' do + let(:cursor_type) { :tailable } + + it 'omits maxTimeMS' do + expect(body[:maxTimeMS]).to be_nil + end + end + + context 'when cursor is tailable_await' do + let(:cursor_type) { :tailable_await } + + context 'when max_await_time_ms is omitted' do + it 'omits maxTimeMS' do + expect(body[:maxTimeMS]).to be_nil + end + end + + context 'when max_await_time_ms is given' do + let(:max_await_time_ms) { 1_234 } + + it 'sets maxTimeMS' do + expect(body[:maxTimeMS]).to be == 1_234 + end + end + end + end + end +end diff --git a/spec/mongo/operation/insert/op_msg_spec.rb b/spec/mongo/operation/insert/op_msg_spec.rb index 9da07f2ab6..9b9e28cc17 100644 --- a/spec/mongo/operation/insert/op_msg_spec.rb +++ b/spec/mongo/operation/insert/op_msg_spec.rb @@ -2,8 +2,12 @@ # rubocop:todo all require 'spec_helper' +require_relative '../shared/csot/examples' describe Mongo::Operation::Insert::OpMsg do + include CSOT::Examples + + let(:context) { Mongo::Operation::Context.new } let(:documents) { [{ :_id => 1, :foo => 1 }] } let(:session) { nil } @@ -104,193 +108,186 @@ # https://jira.mongodb.org/browse/RUBY-2224 require_no_linting - context 'when the server supports OP_MSG' do - min_server_fcv '3.6' + let(:documents) do + [ { foo: 1 }, { bar: 2 }] + end + + let(:global_args) do + { + insert: TEST_COLL, + ordered: true, + writeConcern: write_concern.options, + '$db' => SpecConfig.instance.test_db, + lsid: session.session_id + } + end + + let!(:expected_payload_1) do + Mongo::Protocol::Msg::Section1.new('documents', op.documents) + end - let(:documents) do - [ { foo: 1 }, { bar: 2 }] + let(:session) do + Mongo::Session.new(nil, authorized_client, implicit: true).tap do |session| + allow(session).to receive(:session_id).and_return(42) end + end - let(:global_args) do - { - insert: TEST_COLL, - ordered: true, - writeConcern: write_concern.options, - '$db' => SpecConfig.instance.test_db, - lsid: session.session_id - } + context 'when the topology is replica set or sharded' do + require_topology :replica_set, :sharded + + let(:expected_global_args) do + global_args.merge(Mongo::Operation::CLUSTER_TIME => authorized_client.cluster.cluster_time) + end + + it 'creates the correct OP_MSG message' do + authorized_client.command(ping:1) + RSpec::Mocks.with_temporary_scope do + expect(Mongo::Protocol::Msg).to receive(:new).with([], + {}, + expected_global_args, + expected_payload_1) + op.send(:message, connection) + end end + end + + context 'when the topology is standalone' do + require_topology :single - let!(:expected_payload_1) do - Mongo::Protocol::Msg::Section1.new('documents', op.documents) + let(:expected_global_args) do + global_args end - let(:session) do - Mongo::Session.new(nil, authorized_client, implicit: true).tap do |session| - allow(session).to receive(:session_id).and_return(42) + it 'creates the correct OP_MSG message' do + RSpec::Mocks.with_temporary_scope do + authorized_client.command(ping:1) + expect(Mongo::Protocol::Msg).to receive(:new).with([], + {}, + expected_global_args, + expected_payload_1) + op.send(:message, connection) end end - context 'when the topology is replica set or sharded' do - min_server_fcv '3.6' - require_topology :replica_set, :sharded + context 'when an implicit session is created and the topology is then updated and the server does not support sessions' do + # Mocks on features are incompatible with linting + require_no_linting let(:expected_global_args) do - global_args.merge(Mongo::Operation::CLUSTER_TIME => authorized_client.cluster.cluster_time) + global_args.dup.tap do |args| + args.delete(:lsid) + end + end + + before do + session.implicit?.should be true end it 'creates the correct OP_MSG message' do - authorized_client.command(ping:1) RSpec::Mocks.with_temporary_scope do + expect(connection.features).to receive(:sessions_enabled?).and_return(false) + + expect(expected_global_args).not_to have_key(:lsid) expect(Mongo::Protocol::Msg).to receive(:new).with([], - {}, - expected_global_args, - expected_payload_1) + {}, + expected_global_args, + expected_payload_1) op.send(:message, connection) end end end + end - context 'when the topology is standalone' do - min_server_fcv '3.6' - require_topology :single + context 'when the write concern is 0' do - let(:expected_global_args) do - global_args - end + let(:write_concern) do + Mongo::WriteConcern.get(w: 0) + end - it 'creates the correct OP_MSG message' do - RSpec::Mocks.with_temporary_scope do - authorized_client.command(ping:1) - expect(Mongo::Protocol::Msg).to receive(:new).with([], - {}, - expected_global_args, - expected_payload_1) - op.send(:message, connection) + context 'when the session is implicit' do + + let(:session) do + Mongo::Session.new(nil, authorized_client, implicit: true).tap do |session| + allow(session).to receive(:session_id).and_return(42) + session.should be_implicit end end - context 'when an implicit session is created and the topology is then updated and the server does not support sessions' do - # Mocks on features are incompatible with linting - require_no_linting + context 'when the topology is replica set or sharded' do + require_topology :replica_set, :sharded let(:expected_global_args) do global_args.dup.tap do |args| args.delete(:lsid) + args.merge!(Mongo::Operation::CLUSTER_TIME => authorized_client.cluster.cluster_time) end end - before do - session.implicit?.should be true - end - - it 'creates the correct OP_MSG message' do + it 'does not send a session id in the command' do + authorized_client.command(ping:1) RSpec::Mocks.with_temporary_scope do - expect(connection.features).to receive(:sessions_enabled?).and_return(false) - - expect(expected_global_args).not_to have_key(:lsid) - expect(Mongo::Protocol::Msg).to receive(:new).with([], - {}, - expected_global_args, - expected_payload_1) + expect(Mongo::Protocol::Msg).to receive(:new).with([:more_to_come], + {}, + expected_global_args, + expected_payload_1) op.send(:message, connection) end end end - end - context 'when the write concern is 0' do - - let(:write_concern) do - Mongo::WriteConcern.get(w: 0) - end + context 'when the topology is standalone' do + require_topology :single - context 'when the session is implicit' do - - let(:session) do - Mongo::Session.new(nil, authorized_client, implicit: true).tap do |session| - allow(session).to receive(:session_id).and_return(42) - session.should be_implicit - end - end - - context 'when the topology is replica set or sharded' do - min_server_fcv '3.6' - require_topology :replica_set, :sharded - - let(:expected_global_args) do - global_args.dup.tap do |args| - args.delete(:lsid) - args.merge!(Mongo::Operation::CLUSTER_TIME => authorized_client.cluster.cluster_time) - end - end - - it 'does not send a session id in the command' do - authorized_client.command(ping:1) - RSpec::Mocks.with_temporary_scope do - expect(Mongo::Protocol::Msg).to receive(:new).with([:more_to_come], - {}, - expected_global_args, - expected_payload_1) - op.send(:message, connection) - end + let(:expected_global_args) do + global_args.dup.tap do |args| + args.delete(:lsid) end end - context 'when the topology is standalone' do - min_server_fcv '3.6' - require_topology :single - - let(:expected_global_args) do - global_args.dup.tap do |args| - args.delete(:lsid) - end - end - - it 'creates the correct OP_MSG message' do - authorized_client.command(ping:1) - RSpec::Mocks.with_temporary_scope do - expect(Mongo::Protocol::Msg).to receive(:new).with([:more_to_come], - {}, - expected_global_args, - expected_payload_1) - op.send(:message, connection) - end + it 'creates the correct OP_MSG message' do + authorized_client.command(ping:1) + RSpec::Mocks.with_temporary_scope do + expect(Mongo::Protocol::Msg).to receive(:new).with([:more_to_come], + {}, + expected_global_args, + expected_payload_1) + op.send(:message, connection) end end end + end - context 'when the session is explicit' do - min_server_fcv '3.6' - require_topology :replica_set, :sharded + context 'when the session is explicit' do + require_topology :replica_set, :sharded - let(:session) do - authorized_client.start_session - end + let(:session) do + authorized_client.start_session + end - before do - session.should_not be_implicit - end + before do + session.should_not be_implicit + end - let(:expected_global_args) do - global_args.dup.tap do |args| - args.delete(:lsid) - args.merge!(Mongo::Operation::CLUSTER_TIME => authorized_client.cluster.cluster_time) - end + let(:expected_global_args) do + global_args.dup.tap do |args| + args.delete(:lsid) + args.merge!(Mongo::Operation::CLUSTER_TIME => authorized_client.cluster.cluster_time) end + end - it 'does not send a session id in the command' do - authorized_client.command(ping:1) - RSpec::Mocks.with_temporary_scope do - expect(Mongo::Protocol::Msg).to receive(:new).with([:more_to_come], - {}, - expected_global_args, - expected_payload_1) - op.send(:message, connection) - end + it 'does not send a session id in the command' do + authorized_client.command(ping:1) + RSpec::Mocks.with_temporary_scope do + expect(Mongo::Protocol::Msg).to receive(:new).with([:more_to_come], + {}, + expected_global_args, + expected_payload_1) + op.send(:message, connection) end end end end end + + it_behaves_like 'a CSOT-compliant OpMsg subclass' end diff --git a/spec/mongo/operation/shared/csot/examples.rb b/spec/mongo/operation/shared/csot/examples.rb new file mode 100644 index 0000000000..24c43a43d1 --- /dev/null +++ b/spec/mongo/operation/shared/csot/examples.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true +# rubocop:todo all + +module CSOT + module Examples + # expects the following values to be available: + # `op` -- an instance of an OpMsgBase subclass + def self.included(example_context) + example_context.shared_examples 'mock CSOT environment' do + # Linting freaks out because of the doubles used in these specs. + require_no_linting + + let(:message) { op.send(:message, connection) } + + let(:body) { message.documents.first } + + let(:cursor_type) { nil } + let(:timeout_mode) { nil } + let(:remaining_timeout_sec) { nil } + let(:minimum_round_trip_time) { 0 } + let(:view_options) { {} } + let(:max_await_time_ms) { nil } + + let(:view) do + instance_double(Mongo::Collection::View).tap do |view| + allow(view).to receive(:cursor_type).and_return(cursor_type) + allow(view).to receive(:timeout_mode).and_return(timeout_mode) + allow(view).to receive(:options).and_return(view_options) + allow(view).to receive(:max_await_time_ms).and_return(max_await_time_ms) + end + end + + let(:context) do + Mongo::Operation::Context.new(view: view).tap do |context| + allow(context).to receive(:remaining_timeout_sec).and_return(remaining_timeout_sec) + allow(context).to receive(:timeout?).and_return(!remaining_timeout_sec.nil?) + end + end + + let(:server) do + instance_double(Mongo::Server).tap do |server| + allow(server).to receive(:minimum_round_trip_time).and_return(minimum_round_trip_time) + end + end + + let(:address) { Mongo::Address.new('127.0.0.1') } + + let(:description) do + Mongo::Server::Description.new( + address, { Mongo::Operation::Result::OK => 1 } + ) + end + + let(:features) do + Mongo::Server::Description::Features.new( + Mongo::Server::Description::Features::DRIVER_WIRE_VERSIONS, + address + ) + end + + let(:connection) do + instance_double(Mongo::Server::Connection).tap do |conn| + allow(conn).to receive(:server).and_return(server) + allow(conn).to receive(:description).and_return(description) + allow(conn).to receive(:features).and_return(features) + end + end + + before do + # context is normally set when calling `execute` on the operation, + # but since we're not doing that, we have to tell the operation + # what the context is. + op.context = context + end + end + + example_context.shared_examples 'a CSOT-compliant OpMsg subclass' do + include_examples 'mock CSOT environment' + + context 'when no timeout_ms set' do + it 'does not set maxTimeMS' do + expect(body.key?(:maxTimeMS)).to be false + end + end + + context 'when there is enough time to send the message' do + # Ten seconds remaining + let(:remaining_timeout_sec) { 10 } + + # One second RTT + let(:minimum_round_trip_time) { 1 } + + it 'sets the maxTimeMS' do + # Nine seconds + expect(body[:maxTimeMS]).to eq(9_000) + end + end + + context 'when there is not enough time to send the message' do + # Ten seconds remaining + let(:remaining_timeout_sec) { 0.1 } + + # One second RTT + let(:minimum_round_trip_time) { 1 } + + it 'fails with an exception' do + expect { message }.to raise_error(Mongo::Error::TimeoutError) + end + end + end + end + end +end diff --git a/spec/mongo/query_cache_spec.rb b/spec/mongo/query_cache_spec.rb index a644facf14..f5dd875307 100644 --- a/spec/mongo/query_cache_spec.rb +++ b/spec/mongo/query_cache_spec.rb @@ -138,12 +138,19 @@ end describe '#get' do - let(:view) { double("Mongo::Collection::View") } + let(:view) do + double("Mongo::Collection::View").tap do |view| + allow(view).to receive(:client).and_return(client) + allow(view).to receive(:operation_timeouts).and_return({}) + end + end + let(:result) do double("Mongo::Operation::Result").tap do |result| allow(result).to receive(:is_a?).with(Mongo::Operation::Result).and_return(true) end end + let(:server) { double("Mongo::Server") } let(:caching_cursor) { Mongo::CachingCursor.new(view, result, server) } @@ -161,257 +168,268 @@ allow(view).to receive(:limit) { nil } end - context 'when there is no entry in the cache' do - it 'returns nil' do - expect(Mongo::QueryCache.get(**options)).to be_nil - end - end - - context 'when there is an entry in the cache' do - before do - Mongo::QueryCache.set(caching_cursor, **caching_cursor_options) - end - - context 'when that entry has no limit' do - let(:caching_cursor_options) do - { - namespace: 'db.coll', - selector: { field: 'value' }, - } - end - - let(:query_options) do - caching_cursor_options.merge(limit: limit) - end - - context 'when the query has a limit' do - let(:limit) { 5 } - - it 'returns the caching cursor' do - expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) - end - end - - context 'when the query has a limit but negative' do - let(:limit) { -5 } - - it 'returns the caching cursor' do - expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) - end - end - - context 'when the query has no limit' do - let(:limit) { nil } - - it 'returns the caching cursor' do - expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) - end - end - - context 'when the query has a 0 limit' do - let(:limit) { 0 } - - it 'returns the caching cursor' do - expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) - end - end - end - - context 'when that entry has a 0 limit' do - let(:caching_cursor_options) do - { - namespace: 'db.coll', - selector: { field: 'value' }, - limit: 0, - } - end - - let(:query_options) do - caching_cursor_options.merge(limit: limit) - end - - before do - allow(view).to receive(:limit) { 0 } - end - - context 'when the query has a limit' do - let(:limit) { 5 } - - it 'returns the caching cursor' do - expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) - end - end - - context 'when the query has a limit but negative' do - let(:limit) { -5 } - - it 'returns the caching cursor' do - expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) - end - end - - - context 'when the query has no limit' do - let(:limit) { nil } - - it 'returns the caching cursor' do - expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) - end - end - - context 'when the query has a 0 limit' do - let(:limit) { 0 } - - it 'returns the caching cursor' do - expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) - end - end - end - - context 'when that entry has a limit' do - let(:caching_cursor_options) do - { - namespace: 'db.coll', - selector: { field: 'value' }, - limit: 5, - } - end - - let(:query_options) do - caching_cursor_options.merge(limit: limit) - end - + [true, false].each do |load_balancer| + context "when load_balancer is #{load_balancer}" do before do - allow(view).to receive(:limit) { 5 } - end - - context 'and the new query has a smaller limit' do - let(:limit) { 4 } - - it 'returns the caching cursor' do - expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) - end - end - - context 'and the new query has a smaller limit but negative' do - let(:limit) { -4 } - - it 'returns the caching cursor' do - expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + allow(server).to receive(:load_balancer?) { load_balancer } + if load_balancer + allow(result).to receive(:connection) { nil } end end - context 'and the new query has a larger limit' do - let(:limit) { 6 } - + context 'when there is no entry in the cache' do it 'returns nil' do - expect(Mongo::QueryCache.get(**query_options)).to be_nil + expect(Mongo::QueryCache.get(**options)).to be_nil end end - context 'and the new query has a larger limit but negative' do - let(:limit) { -6 } - - it 'returns nil' do - expect(Mongo::QueryCache.get(**query_options)).to be_nil - end - end - - context 'and the new query has the same limit' do - let(:limit) { 5 } - - it 'returns the caching cursor' do - expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) - end - end - - context 'and the new query has the same limit but negative' do - let(:limit) { -5 } - - it 'returns the caching cursor' do - expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + context 'when there is an entry in the cache' do + before do + Mongo::QueryCache.set(caching_cursor, **caching_cursor_options) end - end - - context 'and the new query has no limit' do - let(:limit) { nil } - it 'returns nil' do - expect(Mongo::QueryCache.get(**query_options)).to be_nil + context 'when that entry has no limit' do + let(:caching_cursor_options) do + { + namespace: 'db.coll', + selector: { field: 'value' }, + } + end + + let(:query_options) do + caching_cursor_options.merge(limit: limit) + end + + context 'when the query has a limit' do + let(:limit) { 5 } + + it 'returns the caching cursor' do + expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + end + end + + context 'when the query has a limit but negative' do + let(:limit) { -5 } + + it 'returns the caching cursor' do + expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + end + end + + context 'when the query has no limit' do + let(:limit) { nil } + + it 'returns the caching cursor' do + expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + end + end + + context 'when the query has a 0 limit' do + let(:limit) { 0 } + + it 'returns the caching cursor' do + expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + end + end end - end - context 'and the new query has a 0 limit' do - let(:limit) { 0 } + context 'when that entry has a 0 limit' do + let(:caching_cursor_options) do + { + namespace: 'db.coll', + selector: { field: 'value' }, + limit: 0, + } + end - it 'returns nil' do - expect(Mongo::QueryCache.get(**query_options)).to be_nil - end - end - end - - context 'when that entry has a negative limit' do - let(:caching_cursor_options) do - { - namespace: 'db.coll', - selector: { field: 'value' }, - limit: -5, - } - end + let(:query_options) do + caching_cursor_options.merge(limit: limit) + end - let(:query_options) do - caching_cursor_options.merge(limit: limit) - end + before do + allow(view).to receive(:limit) { 0 } + end - before do - allow(view).to receive(:limit) { -5 } - end + context 'when the query has a limit' do + let(:limit) { 5 } - context 'and the new query has a smaller limit' do - let(:limit) { 4 } + it 'returns the caching cursor' do + expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + end + end - it 'returns the caching cursor' do - expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) - end - end + context 'when the query has a limit but negative' do + let(:limit) { -5 } - context 'and the new query has a larger limit' do - let(:limit) { 6 } + it 'returns the caching cursor' do + expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + end + end - it 'returns nil' do - expect(Mongo::QueryCache.get(**query_options)).to be_nil - end - end - context 'and the new query has the same negative limit' do - let(:limit) { -5 } + context 'when the query has no limit' do + let(:limit) { nil } - it 'returns the caching cursor' do - expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) - end - end + it 'returns the caching cursor' do + expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + end + end - context 'and the new query has the same positive limit' do - let(:limit) { 5 } + context 'when the query has a 0 limit' do + let(:limit) { 0 } - it 'returns the caching cursor' do - expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + it 'returns the caching cursor' do + expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + end + end end - end - - context 'and the new query has no limit' do - let(:limit) { nil } - it 'returns nil' do - expect(Mongo::QueryCache.get(**query_options)).to be_nil + context 'when that entry has a limit' do + let(:caching_cursor_options) do + { + namespace: 'db.coll', + selector: { field: 'value' }, + limit: 5, + } + end + + let(:query_options) do + caching_cursor_options.merge(limit: limit) + end + + before do + allow(view).to receive(:limit) { 5 } + end + + context 'and the new query has a smaller limit' do + let(:limit) { 4 } + + it 'returns the caching cursor' do + expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + end + end + + context 'and the new query has a smaller limit but negative' do + let(:limit) { -4 } + + it 'returns the caching cursor' do + expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + end + end + + context 'and the new query has a larger limit' do + let(:limit) { 6 } + + it 'returns nil' do + expect(Mongo::QueryCache.get(**query_options)).to be_nil + end + end + + context 'and the new query has a larger limit but negative' do + let(:limit) { -6 } + + it 'returns nil' do + expect(Mongo::QueryCache.get(**query_options)).to be_nil + end + end + + context 'and the new query has the same limit' do + let(:limit) { 5 } + + it 'returns the caching cursor' do + expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + end + end + + context 'and the new query has the same limit but negative' do + let(:limit) { -5 } + + it 'returns the caching cursor' do + expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + end + end + + context 'and the new query has no limit' do + let(:limit) { nil } + + it 'returns nil' do + expect(Mongo::QueryCache.get(**query_options)).to be_nil + end + end + + context 'and the new query has a 0 limit' do + let(:limit) { 0 } + + it 'returns nil' do + expect(Mongo::QueryCache.get(**query_options)).to be_nil + end + end end - end - - context 'and the new query has a 0 limit' do - let(:limit) { 0 } - it 'returns nil' do - expect(Mongo::QueryCache.get(**query_options)).to be_nil + context 'when that entry has a negative limit' do + let(:caching_cursor_options) do + { + namespace: 'db.coll', + selector: { field: 'value' }, + limit: -5, + } + end + + let(:query_options) do + caching_cursor_options.merge(limit: limit) + end + + before do + allow(view).to receive(:limit) { -5 } + end + + context 'and the new query has a smaller limit' do + let(:limit) { 4 } + + it 'returns the caching cursor' do + expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + end + end + + context 'and the new query has a larger limit' do + let(:limit) { 6 } + + it 'returns nil' do + expect(Mongo::QueryCache.get(**query_options)).to be_nil + end + end + + context 'and the new query has the same negative limit' do + let(:limit) { -5 } + + it 'returns the caching cursor' do + expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + end + end + + context 'and the new query has the same positive limit' do + let(:limit) { 5 } + + it 'returns the caching cursor' do + expect(Mongo::QueryCache.get(**query_options)).to eq(caching_cursor) + end + end + + context 'and the new query has no limit' do + let(:limit) { nil } + + it 'returns nil' do + expect(Mongo::QueryCache.get(**query_options)).to be_nil + end + end + + context 'and the new query has a 0 limit' do + let(:limit) { 0 } + + it 'returns nil' do + expect(Mongo::QueryCache.get(**query_options)).to be_nil + end + end end end end diff --git a/spec/mongo/retryable_spec.rb b/spec/mongo/retryable_spec.rb index 2c9947816f..d7f6b9697c 100644 --- a/spec/mongo/retryable_spec.rb +++ b/spec/mongo/retryable_spec.rb @@ -76,6 +76,7 @@ def session allow(session).to receive(:pinned_connection_global_id) allow(session).to receive(:starting_transaction?).and_return(false) allow(session).to receive(:materialize) + allow(session).to receive(:with_transaction_deadline).and_return(nil) end end diff --git a/spec/mongo/server/round_trip_time_averager_spec.rb b/spec/mongo/server/round_trip_time_averager_spec.rb deleted file mode 100644 index 9e885d2246..0000000000 --- a/spec/mongo/server/round_trip_time_averager_spec.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true -# rubocop:todo all - -require 'spec_helper' - -describe Mongo::Server::RoundTripTimeAverager do - let(:averager) { Mongo::Server::RoundTripTimeAverager.new } - - describe '#update_average_round_trip_time' do - context 'no existing average rtt' do - it 'updates average rtt' do - averager.instance_variable_set('@last_round_trip_time', 5) - averager.send(:update_average_round_trip_time) - expect(averager.average_round_trip_time).to eq(5) - end - end - - context 'with existing average rtt' do - it 'averages with existing average rtt' do - averager.instance_variable_set('@last_round_trip_time', 5) - averager.instance_variable_set('@average_round_trip_time', 10) - averager.send(:update_average_round_trip_time) - expect(averager.average_round_trip_time).to eq(9) - end - end - end - - describe '#measure' do - context 'block does not raise' do - it 'updates average rtt' do - expect(averager).to receive(:update_average_round_trip_time) - averager.measure do - end - end - end - - context 'block raises' do - it 'does not update average rtt' do - expect(averager).not_to receive(:update_average_round_trip_time) - lambda do - averager.measure do - raise "Problem" - end - end.should raise_error(/Problem/) - end - end - end -end diff --git a/spec/mongo/server/round_trip_time_calculator_spec.rb b/spec/mongo/server/round_trip_time_calculator_spec.rb new file mode 100644 index 0000000000..ce4dbd39ad --- /dev/null +++ b/spec/mongo/server/round_trip_time_calculator_spec.rb @@ -0,0 +1,120 @@ +# frozen_string_literal: true +# rubocop:todo all + +require 'spec_helper' + +describe Mongo::Server::RoundTripTimeCalculator do + let(:calculator) { Mongo::Server::RoundTripTimeCalculator.new } + + describe '#update_average_round_trip_time' do + context 'no existing average rtt' do + it 'updates average rtt' do + calculator.instance_variable_set('@last_round_trip_time', 5) + calculator.update_average_round_trip_time + expect(calculator.average_round_trip_time).to eq(5) + end + end + + context 'with existing average rtt' do + it 'averages with existing average rtt' do + calculator.instance_variable_set('@last_round_trip_time', 5) + calculator.instance_variable_set('@average_round_trip_time', 10) + calculator.update_average_round_trip_time + expect(calculator.average_round_trip_time).to eq(9) + end + end + end + + describe '#update_minimum_round_trip_time' do + context 'with no samples' do + it 'sets minimum_round_trip_time to zero' do + calculator.update_minimum_round_trip_time + expect(calculator.minimum_round_trip_time).to eq(0) + end + end + + context 'with one sample' do + before do + calculator.instance_variable_set('@last_round_trip_time', 5) + end + + it 'sets minimum_round_trip_time to zero' do + calculator.update_minimum_round_trip_time + expect(calculator.minimum_round_trip_time).to eq(0) + end + end + + context 'with two samples' do + before do + calculator.instance_variable_set('@last_round_trip_time', 10) + calculator.instance_variable_set('@rtts', [5]) + end + + it 'sets minimum_round_trip_time to zero' do + calculator.update_minimum_round_trip_time + expect(calculator.minimum_round_trip_time).to eq(0) + end + end + + context 'with samples less than maximum' do + before do + calculator.instance_variable_set('@last_round_trip_time', 10) + calculator.instance_variable_set('@rtts', [5, 4, 120]) + end + + it 'properly sets minimum_round_trip_time' do + calculator.update_minimum_round_trip_time + expect(calculator.minimum_round_trip_time).to eq(4) + end + end + + context 'with more than maximum samples' do + before do + calculator.instance_variable_set('@last_round_trip_time', 2) + calculator.instance_variable_set('@rtts', [1, 20, 15, 4, 5, 6, 7, 39, 8, 4]) + end + + it 'properly sets minimum_round_trip_time' do + calculator.update_minimum_round_trip_time + expect(calculator.minimum_round_trip_time).to eq(2) + end + end + + end + + describe '#measure' do + context 'block does not raise' do + it 'updates average rtt' do + expect(calculator).to receive(:update_average_round_trip_time) + calculator.measure do + end + end + + it 'updates minimum rtt' do + expect(calculator).to receive(:update_minimum_round_trip_time) + calculator.measure do + end + end + end + + context 'block raises' do + it 'does not update average rtt' do + expect(calculator).not_to receive(:update_average_round_trip_time) + expect do + calculator.measure do + raise "Problem" + end + end.to raise_error(/Problem/) + end + + it 'does not update minimum rtt' do + expect(calculator).not_to receive(:update_minimum_round_trip_time) + expect do + calculator.measure do + raise "Problem" + end + end.to raise_error(/Problem/) + end + end + end +end diff --git a/spec/mongo/socket/ssl_spec.rb b/spec/mongo/socket/ssl_spec.rb index ecdb74e06a..28be3ce78f 100644 --- a/spec/mongo/socket/ssl_spec.rb +++ b/spec/mongo/socket/ssl_spec.rb @@ -103,16 +103,6 @@ expect(socket).to be_alive end end - - context 'when connecting the tcp socket raises an exception' do - - it 'raises an exception' do - expect_any_instance_of(::Socket).to receive(:connect).and_raise(Mongo::Error::SocketTimeoutError) - expect do - socket - end.to raise_error(Mongo::Error::SocketTimeoutError) - end - end end context 'when a certificate and key are provided as strings' do diff --git a/spec/runners/change_streams/test.rb b/spec/runners/change_streams/test.rb index e7b2799b6a..756e7e8fda 100644 --- a/spec/runners/change_streams/test.rb +++ b/spec/runners/change_streams/test.rb @@ -111,7 +111,7 @@ def teardown_test def run change_stream = begin @target.watch(@pipeline, ::Utils.snakeize_hash(@options)) - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e return { result: { error: { @@ -146,7 +146,7 @@ def run begin change = enum.next changes << change - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e return { result: { error: { diff --git a/spec/runners/crud/operation.rb b/spec/runners/crud/operation.rb index 8e93bccd2d..569a169a01 100644 --- a/spec/runners/crud/operation.rb +++ b/spec/runners/crud/operation.rb @@ -355,7 +355,7 @@ def assert_index_not_exists(client, context) if coll.indexes.map { |doc| doc['name'] }.include?(ixn = arguments.fetch('index')) raise "Index #{ixn} exists in collection #{cn} in database #{dn}, but must not" end - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e if e.to_s =~ /ns does not exist/ # Success. else diff --git a/spec/runners/crud/verifier.rb b/spec/runners/crud/verifier.rb index 4c69909618..23dad94440 100644 --- a/spec/runners/crud/verifier.rb +++ b/spec/runners/crud/verifier.rb @@ -134,7 +134,7 @@ def verify_result(expected, actual) expect(actual).not_to be nil when Hash if actual.is_a?(Hash) && actual['error'] && - !expected.keys.any? { |key| key.start_with?('error') } + !expected.keys.any? { |key| key.start_with?('error') || key == 'isTimeoutError' } then raise RSpec::Expectations::ExpectationNotMetError.new, "Expected operation not to fail but it failed: #{actual.inspect}" @@ -143,6 +143,8 @@ def verify_result(expected, actual) expected.each do |k, v| case k + when 'isTimeoutError' + expect(actual['errorContains']).to eq('Mongo::Error::TimeoutError') when 'errorContains' expect(actual['errorContains'].downcase).to include(v.downcase) when 'errorLabelsContain' diff --git a/spec/runners/transactions/operation.rb b/spec/runners/transactions/operation.rb index 3f74c65305..afd4282ad0 100644 --- a/spec/runners/transactions/operation.rb +++ b/spec/runners/transactions/operation.rb @@ -43,12 +43,10 @@ def execute(target, context) end result - rescue Mongo::Error::OperationFailure => e - result = e.instance_variable_get(:@result) - if result.nil? - raise "OperationFailure had nil result: #{e}" - end - err_doc = result.send(:first_document) + rescue Mongo::Error::OperationFailure::Family => e + raise "OperationFailure had nil result: #{e}" if e.result.nil? + + err_doc = e.result.send(:first_document) error_code_name = err_doc['codeName'] || err_doc['writeConcernError'] && err_doc['writeConcernError']['codeName'] if error_code_name.nil? # Sometimes the server does not return the error code name, diff --git a/spec/runners/unified.rb b/spec/runners/unified.rb index 7ab3b1904c..042b1c3947 100644 --- a/spec/runners/unified.rb +++ b/spec/runners/unified.rb @@ -72,7 +72,7 @@ def define_unified_spec_tests(base_path, paths, expect_failure: false) test.assert_events # HACK: other errors are possible and likely will need to # be added here later as the tests evolve. - rescue Mongo::Error::OperationFailure, Unified::Error::UnsupportedOperation, UsingHash::UsingHashKeyError, Unified::Error::EntityMissing + rescue Mongo::Error::OperationFailure::Family, Unified::Error::UnsupportedOperation, UsingHash::UsingHashKeyError, Unified::Error::EntityMissing rescue => e fail "Expected to raise Mongo::Error::OperationFailure or Unified::Error::UnsupportedOperation or UsingHash::UsingHashKeyError or Unified::Error::EntityMissing, got #{e.class}: #{e}" else diff --git a/spec/runners/unified/ambiguous_operations.rb b/spec/runners/unified/ambiguous_operations.rb new file mode 100644 index 0000000000..c83364aa0f --- /dev/null +++ b/spec/runners/unified/ambiguous_operations.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Unified + module AmbiguousOperations + def find(op) + entities.get(:collection, op['object']) + crud_find(op) + rescue Unified::Error::EntityMissing + entities.get(:bucket, op['object']) + gridfs_find(op) + end + end +end diff --git a/spec/runners/unified/assertions.rb b/spec/runners/unified/assertions.rb index 908fe80fbb..69cb2282ad 100644 --- a/spec/runners/unified/assertions.rb +++ b/spec/runners/unified/assertions.rb @@ -379,6 +379,10 @@ def assert_value_matches(actual, expected, msg) unless actual == result raise Error::ResultMismatch, "Actual value #{actual} does not match entity #{expected_v} with value #{result}" end + when '$$lte' + if actual.nil? || actual >= expected_v + raise Error::ResultMismatch, "Actual value #{actual} should be less than #{expected_v}" + end else raise NotImplementedError, "Unknown operator #{operator}" end diff --git a/spec/runners/unified/change_stream_operations.rb b/spec/runners/unified/change_stream_operations.rb index 343486db04..ade607f40c 100644 --- a/spec/runners/unified/change_stream_operations.rb +++ b/spec/runners/unified/change_stream_operations.rb @@ -10,22 +10,9 @@ def create_change_stream(op) object = entities.get_any(object_id) use_arguments(op) do |args| pipeline = args.use!('pipeline') - opts = {} - if batch_size = args.use('batchSize') - opts[:batch_size] = batch_size - end - if comment = args.use('comment') - opts[:comment] = comment - end - if full_document = args.use('fullDocument') - opts[:full_document] = full_document - end - if full_document_before_change = args.use('fullDocumentBeforeChange') - opts[:full_document_before_change] = full_document_before_change - end - if args.key?('showExpandedEvents') - opts[:show_expanded_events] = args.use!('showExpandedEvents') - end + opts = extract_options(args, 'batchSize', 'comment', 'fullDocument', + 'fullDocumentBeforeChange', 'showExpandedEvents', 'timeoutMS', + 'maxAwaitTimeMS') cs = object.watch(pipeline, **opts) if name = op.use('saveResultAsEntity') entities.set(:change_stream, name, cs) @@ -35,18 +22,21 @@ def create_change_stream(op) def iterate_until_document_or_error(op) object_id = op.use!('object') - object = entities.get(:change_stream, object_id) - object.to_enum.next + object = entities.get_any(object_id) + object.try_next + end + + def iterate_once(op) + stream_id = op.use!('object') + stream = entities.get_any(stream_id) + stream.try_next end def close(op) object_id = op.use!('object') - # The Ruby driver unified spec runner does not currently implement - # find cursors as created by createFindCursor. This will be done - # as part of CSOT implementation. When this is done, the line(s) below - # should be changed to retrieve such cursor instances and close them. - object = entities.get(:csot_cursor, object_id) - object.close + opts = op.key?('arguments') ? extract_options(op.use!('arguments'), 'timeoutMS') : {} + object = entities.get_any(object_id) + object.close(opts) end end end diff --git a/spec/runners/unified/crud_operations.rb b/spec/runners/unified/crud_operations.rb index 69e35513a8..fb2387ce6e 100644 --- a/spec/runners/unified/crud_operations.rb +++ b/spec/runners/unified/crud_operations.rb @@ -5,7 +5,7 @@ module Unified module CrudOperations - def find(op) + def crud_find(op) get_find_view(op).to_a end @@ -16,35 +16,21 @@ def find_one(op) def get_find_view(op) collection = entities.get(:collection, op.use!('object')) use_arguments(op) do |args| - opts = { - let: args.use('let'), - comment: args.use('comment'), - allow_disk_use: args.use('allowDiskUse'), - show_disk_loc: args.use('showRecordId'), - return_key: args.use('returnKey'), - projection: args.use('projection'), - skip: args.use('skip'), - hint: args.use('hint'), - max_value: args.use('max'), - max_time_ms: args.use('maxTimeMS'), - min_value: args.use('min'), - } - if session = args.use('session') - opts[:session] = entities.get(:session, session) - end - if collation = args.use('collation') - opts[:collation] = collation - end - if args.key?('noCursorTimeout') - opts[:no_cursor_timeout] = args.use('noCursorTimeout') - end - if args.key?('oplogReplay') - opts[:oplog_replay] = args.use('oplogReplay') - end - if args.key?('allowPartialResults') - opts[:allow_partial_results] = args.use('allowPartialResults') - end - req = collection.find(args.use!('filter'), **opts) + filter = args.use!('filter') + session = args.use('session') + + opts = extract_options(args, 'let', 'comment', + 'allowDiskUse', 'returnKey', 'projection', + 'skip', 'hint', 'maxTimeMS', 'timeoutMS', + 'collation', 'noCursorTimeout', 'oplogReplay', 'allowPartialResults', + 'timeoutMode', 'maxAwaitTimeMS', 'cursorType', 'timeoutMode', + { 'showRecordId' => :show_disk_loc, 'max' => :max_value, 'min' => :min_value }, + allow_extra: true) + symbolize_options!(opts, :timeout_mode, :cursor_type) + + opts[:session] = entities.get(:session, session) if session + + req = collection.find(filter, **opts) if batch_size = args.use('batchSize') req = req.batch_size(batch_size) end @@ -61,15 +47,23 @@ def get_find_view(op) end end - def count_documents(op) + def count(op) collection = entities.get(:collection, op.use!('object')) use_arguments(op) do |args| - opts = {} + opts = extract_options(args, 'comment', 'timeoutMS', 'maxTimeMS', allow_extra: true) if session = args.use('session') opts[:session] = entities.get(:session, session) end - if comment = args.use('comment') - opts[:comment] = comment + collection.count(args.use!('filter'), **opts) + end + end + + def count_documents(op) + collection = entities.get(:collection, op.use!('object')) + use_arguments(op) do |args| + opts = extract_options(args, 'comment', 'timeoutMS', 'maxTimeMS', allow_extra: true) + if session = args.use('session') + opts[:session] = entities.get(:session, session) end collection.find(args.use!('filter')).count_documents(**opts) end @@ -78,12 +72,9 @@ def count_documents(op) def estimated_document_count(op) collection = entities.get(:collection, op.use!('object')) use_arguments(op) do |args| - opts = {} - if max_time_ms = args.use('maxTimeMS') - opts[:max_time_ms] = max_time_ms - end - if comment = args.use('comment') - opts[:comment] = comment + opts = extract_options(args, 'comment', 'timeoutMS', 'maxTimeMS', allow_extra: true) + if session = args.use('session') + opts[:session] = entities.get(:session, session) end collection.estimated_document_count(**opts) end @@ -92,13 +83,10 @@ def estimated_document_count(op) def distinct(op) collection = entities.get(:collection, op.use!('object')) use_arguments(op) do |args| - opts = {} + opts = extract_options(args, 'comment', 'timeoutMS', 'maxTimeMS', allow_extra: true) if session = args.use('session') opts[:session] = entities.get(:session, session) end - if comment = args.use('comment') - opts[:comment] = comment - end req = collection.find(args.use!('filter'), **opts).distinct(args.use!('fieldName'), **opts) result = req.to_a end @@ -114,6 +102,8 @@ def find_one_and_update(op) comment: args.use('comment'), hint: args.use('hint'), upsert: args.use('upsert'), + timeout_ms: args.use('timeoutMS'), + max_time_ms: args.use('maxTimeMS') } if return_document = args.use('returnDocument') opts[:return_document] = return_document.downcase.to_sym @@ -134,6 +124,8 @@ def find_one_and_replace(op) let: args.use('let'), comment: args.use('comment'), hint: args.use('hint'), + timeout_ms: args.use('timeoutMS'), + max_time_ms: args.use('maxTimeMS') } if session = args.use('session') opts[:session] = entities.get(:session, session) @@ -150,6 +142,8 @@ def find_one_and_delete(op) let: args.use('let'), comment: args.use('comment'), hint: args.use('hint'), + timeout_ms: args.use('timeoutMS'), + max_time_ms: args.use('maxTimeMS') } if session = args.use('session') opts[:session] = entities.get(:session, session) @@ -162,7 +156,9 @@ def insert_one(op) collection = entities.get(:collection, op.use!('object')) use_arguments(op) do |args| opts = { - comment: args.use('comment') + comment: args.use('comment'), + timeout_ms: args.use('timeoutMS'), + max_time_ms: args.use('maxTimeMS') } if session = args.use('session') opts[:session] = entities.get(:session, session) @@ -175,7 +171,9 @@ def insert_many(op) collection = entities.get(:collection, op.use!('object')) use_arguments(op) do |args| opts = { - comment: args.use('comment') + comment: args.use('comment'), + timeout_ms: args.use('timeoutMS'), + max_time_ms: args.use('maxTimeMS') } unless (ordered = args.use('ordered')).nil? opts[:ordered] = ordered @@ -195,6 +193,8 @@ def update_one(op) comment: args.use('comment'), hint: args.use('hint'), upsert: args.use('upsert'), + timeout_ms: args.use('timeoutMS'), + max_time_ms: args.use('maxTimeMS') } if session = args.use('session') opts[:session] = entities.get(:session, session) @@ -210,6 +210,8 @@ def update_many(op) let: args.use('let'), comment: args.use('comment'), hint: args.use('hint'), + timeout_ms: args.use('timeoutMS'), + max_time_ms: args.use('maxTimeMS') } collection.update_many(args.use!('filter'), args.use!('update'), **opts) end @@ -224,7 +226,9 @@ def replace_one(op) comment: args.use('comment'), upsert: args.use('upsert'), let: args.use('let'), - hint: args.use('hint') + hint: args.use('hint'), + timeout_ms: args.use('timeoutMS'), + max_time_ms: args.use('maxTimeMS') ) end end @@ -236,6 +240,8 @@ def delete_one(op) let: args.use('let'), comment: args.use('comment'), hint: args.use('hint'), + timeout_ms: args.use('timeoutMS'), + max_time_ms: args.use('maxTimeMS') } if session = args.use('session') opts[:session] = entities.get(:session, session) @@ -251,6 +257,8 @@ def delete_many(op) let: args.use('let'), comment: args.use('comment'), hint: args.use('hint'), + timeout_ms: args.use('timeoutMS'), + max_time_ms: args.use('maxTimeMS') } collection.delete_many(args.use!('filter'), **opts) end @@ -272,6 +280,12 @@ def bulk_write(op) if let = args.use('let') opts[:let] = let end + if timeout_ms = args.use('timeoutMS') + opts[:timeout_ms] = timeout_ms + end + if max_time_ms = args.use('maxTimeMS') + opts[:max_time_ms] = max_time_ms + end collection.bulk_write(requests, **opts) end end @@ -280,27 +294,36 @@ def aggregate(op) obj = entities.get_any(op.use!('object')) args = op.use!('arguments') pipeline = args.use!('pipeline') - opts = { - let: args.use('let'), - } + + opts = extract_options(args, 'let', 'comment', 'batchSize', 'maxTimeMS', + 'allowDiskUse', 'timeoutMode', 'timeoutMS', 'maxTimeMS', allow_extra: true) + symbolize_options!(opts, :timeout_mode) + if session = args.use('session') opts[:session] = entities.get(:session, session) end - if comment = args.use('comment') - opts[:comment] = comment - end - if batch_size = args.use('batchSize') - opts[:batch_size] = batch_size - end - if args.key?('allowDiskUse') - opts[:allow_disk_use] = args.use('allowDiskUse') - end + unless args.empty? raise NotImplementedError, "Unhandled spec keys: #{args} in #{test_spec}" end + obj.aggregate(pipeline, **opts).to_a end + def create_find_cursor(op) + obj = entities.get_any(op.use!('object')) + args = op.use!('arguments') + + filter = args.use('filter') + opts = extract_options(args, 'batchSize', 'timeoutMS', 'cursorType', 'maxAwaitTimeMS') + symbolize_options!(opts, :cursor_type) + + view = obj.find(filter, opts) + view.each # to initialize the cursor + + view.cursor + end + private def convert_bulk_write_spec(spec) diff --git a/spec/runners/unified/ddl_operations.rb b/spec/runners/unified/ddl_operations.rb index 185a251b7a..bdfa916a1f 100644 --- a/spec/runners/unified/ddl_operations.rb +++ b/spec/runners/unified/ddl_operations.rb @@ -20,6 +20,9 @@ def list_dbs(op, name_only: false) if session = args.use('session') opts[:session] = entities.get(:session, session) end + if timeout_ms = args.use('timeoutMS') + opts[:timeout_ms] = timeout_ms + end client.list_databases(args.use('filter') || {}, name_only, **opts) end end @@ -50,6 +53,15 @@ def create_collection(op) if pipeline = args.use('pipeline') collection_opts[:pipeline] = pipeline end + if capped = args.use('capped') + collection_opts[:capped] = capped + end + if size = args.use('size') + collection_opts[:size] = size + end + if max = args.use('max') + collection_opts[:max] = max + end database[args.use!('collection'), collection_opts].create(**opts) end end @@ -65,13 +77,16 @@ def list_collection_names(op) def list_colls(op, name_only: false) database = entities.get(:database, op.use!('object')) use_arguments(op) do |args| - opts = {} + opts = extract_options(args, 'filter', 'timeoutMode', allow_extra: true) + symbolize_options!(opts, :timeout_mode) + if session = args.use('session') opts[:session] = entities.get(:session, session) end - if filter = args.use('filter') - opts[:filter] = filter + if timeout_ms = args.use('timeoutMS') + opts[:timeout_ms] = timeout_ms end + database.list_collections(**opts.merge(name_only: name_only)) end end @@ -126,14 +141,25 @@ def assert_collection_not_exists(op) def list_indexes(op) collection = entities.get(:collection, op.use!('object')) use_arguments(op) do |args| - opts = {} + opts = extract_options(args, 'timeoutMode', allow_extra: true) if session = args.use('session') opts[:session] = entities.get(:session, session) end + if timeout_ms = args.use('timeoutMS') + opts[:timeout_ms] = timeout_ms + end collection.indexes(**opts).to_a end end + def drop_indexes(op) + collection = entities.get(:collection, op.use!('object')) + use_arguments(op) do |args| + opts = extract_options(args, 'maxTimeMS', 'timeoutMS', allow_extra: true) + collection.indexes.drop_all(**opts) + end + end + def create_index(op) collection = entities.get(:collection, op.use!('object')) use_arguments(op) do |args| @@ -144,7 +170,12 @@ def create_index(op) if args.key?('unique') opts[:unique] = args.use('unique') end - + if timeout_ms = args.use('timeoutMS') + opts[:timeout_ms] = timeout_ms + end + if max_time_ms = args.use('maxTimeMS') + opts[:max_time_ms] = max_time_ms + end collection.indexes.create_one( args.use!('keys'), name: args.use('name'), @@ -156,7 +187,7 @@ def create_index(op) def drop_index(op) collection = entities.get(:collection, op.use!('object')) use_arguments(op) do |args| - opts = {} + opts = extract_options(args, 'maxTimeMS', 'timeoutMS', allow_extra: true) if session = args.use('session') opts[:session] = entities.get(:session, session) end @@ -188,7 +219,7 @@ def assert_index_not_exists(op) begin index = collection.indexes.get(args.use!('indexName')) raise Error::ResultMismatch, "Index found" - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e if e.code == 26 # OK else diff --git a/spec/runners/unified/grid_fs_operations.rb b/spec/runners/unified/grid_fs_operations.rb index c5cad546bf..2121eb0fa4 100644 --- a/spec/runners/unified/grid_fs_operations.rb +++ b/spec/runners/unified/grid_fs_operations.rb @@ -5,17 +5,38 @@ module Unified module GridFsOperations + def gridfs_find(op) + bucket = entities.get(:bucket, op.use!('object')) + use_arguments(op) do |args| + filter = args.use!('filter') + + opts = extract_options(args, 'allowDiskUse', + 'skip', 'hint','timeoutMS', + 'noCursorTimeout', 'sort', 'limit') + + bucket.find(filter,opts).to_a + end + end + def delete(op) bucket = entities.get(:bucket, op.use!('object')) use_arguments(op) do |args| - bucket.delete(args.use!('id')) + opts = {} + if timeout_ms = args.use('timeoutMS') + opts[:timeout_ms] = timeout_ms + end + bucket.delete(args.use!('id'), opts) end end def download(op) bucket = entities.get(:bucket, op.use!('object')) use_arguments(op) do |args| - stream = bucket.open_download_stream(args.use!('id')) + opts = {} + if timeout_ms = args.use('timeoutMS') + opts[:timeout_ms] = timeout_ms + end + stream = bucket.open_download_stream(args.use!('id'), opts) stream.read end end @@ -48,6 +69,9 @@ def upload(op) if disable_md5 = args.use('disableMD5') opts[:disable_md5] = disable_md5 end + if timeout_ms = args.use('timeoutMS') + opts[:timeout_ms] = timeout_ms + end contents = transform_contents(args.use!('source')) file_id = nil bucket.open_upload_stream(args.use!('filename'), **opts) do |stream| @@ -58,6 +82,17 @@ def upload(op) end end + def drop(op) + bucket = entities.get(:bucket, op.use!('object')) + use_arguments(op) do |args| + opts = {} + if timeout_ms = args.use('timeoutMS') + opts[:timeout_ms] = timeout_ms + end + bucket.drop(opts) + end + end + private def transform_contents(contents) diff --git a/spec/runners/unified/support_operations.rb b/spec/runners/unified/support_operations.rb index a99d310958..45f3aadc10 100644 --- a/spec/runners/unified/support_operations.rb +++ b/spec/runners/unified/support_operations.rb @@ -20,6 +20,9 @@ def run_command(op) if read_preference = args.use('readPreference') opts[:read] = ::Utils.snakeize_hash(read_preference) end + if timeout_ms = args.use('timeoutMS') + opts[:timeout_ms] = timeout_ms + end database.command(cmd, **opts) end @@ -129,14 +132,20 @@ def assert_session_transaction_state(op) def commit_transaction(op) session = entities.get(:session, op.use!('object')) - assert_no_arguments(op) - session.commit_transaction + opts = {} + use_arguments(op) do |args| + opts[:timeout_ms] = args.use('timeoutMS') + end + session.commit_transaction(opts.compact) end def abort_transaction(op) session = entities.get(:session, op.use!('object')) - assert_no_arguments(op) - session.abort_transaction + opts = {} + use_arguments(op) do |args| + opts[:timeout_ms] = args.use('timeoutMS') + end + session.abort_transaction(opts.compact) end def with_transaction(op) @@ -311,6 +320,36 @@ def assert_number_connections_checked_out(op) private + # @param [ UsingHash ] args the arguments to extract options from + # @param [ Array ] keys an array of strings and Hashes, + # where Hashes represent a mapping from the MDB key to the correspoding + # Ruby key. For Strings, the Ruby key is assumed to be a simple conversion + # of the MDB key, from camel-case to snake-case. + # @param [ true | false ] allow_extra whether or not extra keys are allowed + # to exist in the args hash, beyond those listed. + def extract_options(args, *keys, allow_extra: false) + {}.tap do |opts| + keys.each do |key| + Array(key).each do |mdb_key, ruby_key| + value = args.use(mdb_key) + opts[ruby_key || mdb_name_to_ruby(mdb_key)] = value unless value.nil? + end + end + + raise NotImplementedError, "unhandled keys: #{args}" if !allow_extra && !args.empty? + end + end + + def symbolize_options!(opts, *keys) + keys.each do |key| + opts[key] = mdb_name_to_ruby(opts[key]) if opts[key] + end + end + + def mdb_name_to_ruby(name) + name.to_s.gsub(/([a-z])([A-Z])/) { "#{$1}_#{$2}" }.downcase.to_sym + end + def assert_no_arguments(op) if op.key?('arguments') raise NotimplementedError, "Arguments are not allowed" diff --git a/spec/runners/unified/test.rb b/spec/runners/unified/test.rb index 114088f693..32b0ba0a82 100644 --- a/spec/runners/unified/test.rb +++ b/spec/runners/unified/test.rb @@ -2,6 +2,7 @@ # rubocop:todo all require 'runners/crud/requirement' +require 'runners/unified/ambiguous_operations' require 'runners/unified/client_side_encryption_operations' require 'runners/unified/crud_operations' require 'runners/unified/grid_fs_operations' @@ -17,6 +18,7 @@ module Unified class Test + include AmbiguousOperations include ClientSideEncryptionOperations include CrudOperations include GridFsOperations @@ -42,12 +44,14 @@ def initialize(spec, **opts) if req = @spec['group_runOnRequirements'] @group_reqs = req.map { |r| Mongo::CRUD::Requirement.new(r) } end - mongoses = @spec['createEntities'].select do |spec| - spec['client'] - end.map do |spec| - spec['client']['useMultipleMongoses'] - end.compact.uniq - @multiple_mongoses = mongoses.any? { |v| v } + if @spec['createEntities'] + mongoses = @spec['createEntities'].select do |spec| + spec['client'] + end.map do |spec| + spec['client']['useMultipleMongoses'] + end.compact.uniq + @multiple_mongoses = mongoses.any? { |v| v } + end @test_spec.freeze @subscribers = {} @observe_sensitive = {} @@ -85,6 +89,8 @@ def create_spec_entities end def generate_entities(es) + return if es.nil? + es.each do |entity_spec| unless entity_spec.keys.length == 1 raise NotImplementedError, "Entity must have exactly one key" @@ -329,7 +335,7 @@ def set_initial_data begin collection.create(create_options) rescue Mongo::Error => e - if Mongo::Error::OperationFailure === e && ( + if Mongo::Error::OperationFailure::Family === e && ( e.code == 48 || e.message =~ /collection already exists/ ) # Already exists @@ -411,10 +417,16 @@ def execute_operation(op) public_send(method_name, op) rescue Mongo::Error, bson_error, Mongo::Auth::Unauthorized, ArgumentError => e + if expected_error.use('isTimeoutError') + unless Mongo::Error::TimeoutError === e + raise e + raise Error::ErrorMismatch, %Q,Expected TimeoutError ("isTimeoutError") but got #{e}, + end + end if expected_error.use('isClientError') # isClientError doesn't actually mean a client error. # It means anything other than OperationFailure. DRIVERS-1799 - if Mongo::Error::OperationFailure === e + if Mongo::Error::OperationFailure::Family === e raise Error::ErrorMismatch, %Q,Expected not OperationFailure ("isClientError") but got #{e}, end end @@ -482,7 +494,7 @@ def execute_operation(op) if result.nil? && expected_result.keys == ["$$unsetOrMatches"] return elsif result.nil? && !expected_result.empty? - raise Error::ResultMismatch, "#{msg}: expected #{expected} but got nil" + raise Error::ResultMismatch, "expected #{expected_result} but got nil" elsif Array === expected_result assert_documents_match(result, expected_result) else @@ -536,7 +548,7 @@ def kill_sessions root_authorized_client.command( killAllSessions: [], ) - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e if e.code == 11601 # operation was interrupted, ignore. SERVER-38335 elsif e.code == 13 diff --git a/spec/shared b/spec/shared index cee4bc0264..2fe5724b0a 160000 --- a/spec/shared +++ b/spec/shared @@ -1 +1 @@ -Subproject commit cee4bc02649a573c8256b0505c1d23f503ac2609 +Subproject commit 2fe5724b0a586fbeac602f15f7d43ccc7bbe531b diff --git a/spec/solo/clean_exit_spec.rb b/spec/solo/clean_exit_spec.rb index 80e80fd030..f26e7307b0 100644 --- a/spec/solo/clean_exit_spec.rb +++ b/spec/solo/clean_exit_spec.rb @@ -17,6 +17,8 @@ it 'exits cleanly' do client = Mongo::Client.new(uri) client.database.collection_names.to_a + ensure + client.close end end end diff --git a/spec/spec_tests/client_side_operations_timeout_spec.rb b/spec/spec_tests/client_side_operations_timeout_spec.rb new file mode 100644 index 0000000000..ac5cf53495 --- /dev/null +++ b/spec/spec_tests/client_side_operations_timeout_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'runners/unified' + +base = "#{CURRENT_PATH}/spec_tests/data/client_side_operations_timeout" +CSOT_TESTS = Dir.glob("#{base}/**/*.yml").sort + +describe 'CSOT unified spec tests' do + if [ 1, '1', 'yes', 'true' ].include?(ENV['CSOT_SPEC_TESTS']) + define_unified_spec_tests(base, CSOT_TESTS) + else + skip 'CSOT spec tests are disabled. To enable them set env variable CSOT_SPEC_TESTS to 1' + end +end diff --git a/spec/spec_tests/data/change_streams_unified/change-streams-clusterTime.yml b/spec/spec_tests/data/change_streams_unified/change-streams-clusterTime.yml index b1d9f20e01..a756224aad 100644 --- a/spec/spec_tests/data/change_streams_unified/change-streams-clusterTime.yml +++ b/spec/spec_tests/data/change_streams_unified/change-streams-clusterTime.yml @@ -15,7 +15,9 @@ createEntities: runOnRequirements: - minServerVersion: "4.0.0" - topologies: [ replicaset, load-balanced, sharded ] + # TODO(DRIVERS-2323): Run all possible tests against sharded clusters once we know the + # cause of unexpected command monitoring events. + topologies: [ replicaset ] serverless: forbid initialData: diff --git a/spec/spec_tests/data/change_streams_unified/change-streams-disambiguatedPaths.yml b/spec/spec_tests/data/change_streams_unified/change-streams-disambiguatedPaths.yml index 9ca9abf2e3..260c527036 100644 --- a/spec/spec_tests/data/change_streams_unified/change-streams-disambiguatedPaths.yml +++ b/spec/spec_tests/data/change_streams_unified/change-streams-disambiguatedPaths.yml @@ -15,7 +15,9 @@ createEntities: runOnRequirements: - minServerVersion: "6.1.0" - topologies: [ replicaset, load-balanced, sharded ] + # TODO(DRIVERS-2323): Run all possible tests against sharded clusters once we know the + # cause of unexpected command monitoring events. + topologies: [ replicaset ] serverless: forbid initialData: diff --git a/spec/spec_tests/data/change_streams_unified/change-streams-errors.yml b/spec/spec_tests/data/change_streams_unified/change-streams-errors.yml index 85133dae0a..8de5780aaa 100644 --- a/spec/spec_tests/data/change_streams_unified/change-streams-errors.yml +++ b/spec/spec_tests/data/change_streams_unified/change-streams-errors.yml @@ -3,7 +3,9 @@ description: "change-streams-errors" schemaVersion: "1.7" runOnRequirements: - - serverless: forbid + # TODO(DRIVERS-2323): Run all possible tests against sharded clusters once we know the + # cause of unexpected command monitoring events. + - topologies: [ replicaset ] createEntities: - client: diff --git a/spec/spec_tests/data/change_streams_unified/change-streams-pre_and_post_images.yml b/spec/spec_tests/data/change_streams_unified/change-streams-pre_and_post_images.yml index 6bc58eaf2d..4bfc3c685c 100644 --- a/spec/spec_tests/data/change_streams_unified/change-streams-pre_and_post_images.yml +++ b/spec/spec_tests/data/change_streams_unified/change-streams-pre_and_post_images.yml @@ -4,7 +4,7 @@ schemaVersion: "1.4" runOnRequirements: - minServerVersion: "6.0.0" - topologies: [ replicaset, sharded, load-balanced ] + topologies: [ replicaset ] serverless: forbid createEntities: diff --git a/spec/spec_tests/data/change_streams_unified/change-streams-resume-allowlist.yml b/spec/spec_tests/data/change_streams_unified/change-streams-resume-allowlist.yml index c5b7a874d3..cee6253153 100644 --- a/spec/spec_tests/data/change_streams_unified/change-streams-resume-allowlist.yml +++ b/spec/spec_tests/data/change_streams_unified/change-streams-resume-allowlist.yml @@ -5,7 +5,7 @@ schemaVersion: "1.7" runOnRequirements: - minServerVersion: "3.6" - topologies: [ replicaset, sharded, load-balanced ] + topologies: [ replicaset ] serverless: forbid createEntities: diff --git a/spec/spec_tests/data/change_streams_unified/change-streams-resume-errorLabels.yml b/spec/spec_tests/data/change_streams_unified/change-streams-resume-errorLabels.yml index 5cc6d423a4..691f88e180 100644 --- a/spec/spec_tests/data/change_streams_unified/change-streams-resume-errorLabels.yml +++ b/spec/spec_tests/data/change_streams_unified/change-streams-resume-errorLabels.yml @@ -5,7 +5,7 @@ schemaVersion: "1.7" runOnRequirements: - minServerVersion: "4.3.1" - topologies: [ replicaset, sharded, load-balanced ] + topologies: [ replicaset ] serverless: forbid createEntities: diff --git a/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml b/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml index e6289047bf..3347f69a3d 100644 --- a/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml +++ b/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml @@ -2,7 +2,7 @@ description: "change-streams-showExpandedEvents" schemaVersion: "1.7" runOnRequirements: - minServerVersion: "6.0.0" - topologies: [ replicaset, sharded ] + topologies: [ replicaset ] serverless: forbid createEntities: - client: diff --git a/spec/spec_tests/data/client_side_encryption/badQueries.yml b/spec/spec_tests/data/client_side_encryption/badQueries.yml index 893622c4ce..bdd8627fb4 100644 --- a/spec/spec_tests/data/client_side_encryption/badQueries.yml +++ b/spec/spec_tests/data/client_side_encryption/badQueries.yml @@ -1,5 +1,6 @@ runOn: - minServerVersion: "4.1.10" + topology: [ "replicaset", "sharded" ] database_name: &database_name "default" collection_name: &collection_name "default" @@ -533,4 +534,4 @@ tests: filter: {} fieldName: "encrypted_w_altname" result: - errorContains: "The distinct key is not allowed to be marked for encryption with a non-UUID keyId" \ No newline at end of file + errorContains: "The distinct key is not allowed to be marked for encryption with a non-UUID keyId" diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Aggregate.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Aggregate.yml deleted file mode 100644 index 763ad80b9e..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Aggregate.yml +++ /dev/null @@ -1,242 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$date': {'$numberLong': '0'}}, 'max': {'$date': {'$numberLong': '200'}}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Date. Aggregate." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDate: {$date: { $numberLong: "0" }} } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDate: {$date: { $numberLong: "1" }} } - - name: aggregate - arguments: - pipeline: [{ $match: { "encryptedDate": { $gt: {$date: {$numberLong: "0" }}} } }] - result: [*doc1] - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - aggregate: *collection_name - pipeline: [ - { - "$match": { - "encryptedDate": { - "$gt": { - "$binary": { - "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - } - } - ] - cursor: {} - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: aggregate - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDate": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedDate": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Correctness.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Correctness.yml deleted file mode 100644 index 5e12afd8f5..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Correctness.yml +++ /dev/null @@ -1,423 +0,0 @@ -# Test correctness results. -# Does not include command monitoring expectations or outcome assertions to make tests more readable. - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$date': {'$numberLong': '0'}}, 'max': {'$date': {'$numberLong': '200'}}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "Find with $gt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDate: { $date: { $numberLong: "0" } } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDate: { $date: { $numberLong: "1" } } } - - name: find - arguments: - filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" } } }} - result: [*doc1] - - - description: "Find with $gte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDate: { $gte: { $date: { $numberLong: "0" } } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $gt with no results" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDate: { $gt: { $date: { $numberLong: "1" } } }} - result: [] - - - description: "Find with $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDate: { $lt: { $date: { $numberLong: "1" } } }} - result: [*doc0] - - - description: "Find with $lte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDate: { $lte: { $date: { $numberLong: "1" } } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $lt below min" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDate: { $lt: { $date: { $numberLong: "0" } } }} - result: - errorContains: must be greater than the range minimum - - - description: "Find with $gt above max" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDate: { $gt: { $date: { $numberLong: "200" } } }} - result: - errorContains: must be less than the range maximum - - - description: "Find with $gt and $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" } }, $lt: { $date: {$numberLong: "2"}} }} - result: [*doc1] - - - description: "Find with equality" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDate: { $date: { $numberLong: "0" } } } - result: [*doc0] - - name: find - arguments: - filter: { encryptedDate: { $date: { $numberLong: "1" } } } - result: [*doc1] - - - description: "Find with full range" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDate: { $gte: { $date: {$numberLong: "0"}}, $lte: { $date: {$numberLong: "200"} } } } - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $in" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDate: { $in: [ {$date: {$numberLong: "0"}} ] } } - result: [*doc0] - - - description: "Insert out of range" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: { _id: 0, encryptedDate: {$date: { $numberLong: "-1" }}} - result: - errorContains: value must be greater than or equal to the minimum value - - - description: "Insert min and max" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: *doc0 - - name: insertOne - arguments: - document: &doc200 { _id: 200, encryptedDate: { $date: { $numberLong: "200" } }} - - name: find - arguments: - filter: {} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc200] - - - description: "Aggregate with $gte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDate: { $gte: { $date: { $numberLong: "0" } } }} } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $gt with no results" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDate: { $gt: { $date: { $numberLong: "1" } } }} } - result: [] - - - description: "Aggregate with $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDate: { $lt: { $date: { $numberLong: "1" } } }} } - result: [*doc0] - - - description: "Aggregate with $lte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDate: { $lte: { $date: { $numberLong: "1" } } }} } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $lt below min" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDate: { $lt: { $date: { $numberLong: "0" } } }} } - result: - errorContains: must be greater than the range minimum - - - description: "Aggregate with $gt above max" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDate: { $gt: { $date: { $numberLong: "200" } } }} } - result: - errorContains: must be less than the range maximum - - - description: "Aggregate with $gt and $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDate: { $gt: { $date: { $numberLong: "0" } }, $lt: { $date: {$numberLong: "2"}} }} } - result: [*doc1] - - - description: "Aggregate with equality" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDate: { $date: { $numberLong: "0" } } } } - result: [*doc0] - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDate: { $date: { $numberLong: "1" } } } } - result: [*doc1] - - - description: "Aggregate with full range" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDate: { $gte: {$date: {$numberLong: "0"}}, $lte: {$date: {$numberLong: "200"}} } } } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $in" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDate: { $in: [ {$date: {$numberLong: "0"}} ] } } } - result: [*doc0] - - - description: "Wrong type: Insert Double" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: { _id: 0, encryptedDate: { $numberDouble: "0" }} } - result: - # Expect an error from mongocryptd. - errorContains: "cannot encrypt element" - - - description: "Wrong type: Find Double" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: find - arguments: - filter: { encryptedDate: { $gte: { $numberDouble: "0" } }} - result: - # expect an error mongocryptd. - errorContains: "value type is a date" \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Delete.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Delete.yml deleted file mode 100644 index 6b9a74f72a..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Delete.yml +++ /dev/null @@ -1,183 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$date': {'$numberLong': '0'}}, 'max': {'$date': {'$numberLong': '200'}}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Date. Delete." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDate: {$date: { $numberLong: "0" }} } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDate: {$date: { $numberLong: "1" }} } - - name: deleteOne - arguments: - filter: { "encryptedDate": { $gt: {$date: {$numberLong: "0" }}} } - result: - deletedCount: 1 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - delete: *collection_name - deletes: [ - { - "q": { - "encryptedDate": { - "$gt": { - "$binary": { - "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - }, - "limit": 1 - } - ] - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: delete - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDate": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-FindOneAndUpdate.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-FindOneAndUpdate.yml deleted file mode 100644 index 2deb4aa5c2..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-FindOneAndUpdate.yml +++ /dev/null @@ -1,240 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$date': {'$numberLong': '0'}}, 'max': {'$date': {'$numberLong': '200'}}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Date. FindOneAndUpdate." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDate: {$date: { $numberLong: "0" }} } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDate: {$date: { $numberLong: "1" }} } - - name: findOneAndUpdate - arguments: - filter: { encryptedDate: { $gt: {$date: {$numberLong: "0"}}} } - update: { "$set": { "encryptedDate": {$date: {$numberLong: "2"}}}} - returnDocument: Before - result: *doc1 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - findAndModify: *collection_name - query: { - "encryptedDate": { - "$gt": { - "$binary": { - "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - } - update: { "$set": {"encryptedDate": { $$type: "binData" }} } - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: findAndModify - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDate": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedDate": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-InsertFind.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-InsertFind.yml deleted file mode 100644 index 70b3ecbe5f..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-InsertFind.yml +++ /dev/null @@ -1,236 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$date': {'$numberLong': '0'}}, 'max': {'$date': {'$numberLong': '200'}}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Date. Insert and Find." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDate: { $date: { $numberLong: "0" }} } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDate: { $date: { $numberLong: "1" }} } - - name: find - arguments: - filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" }} } } - result: [*doc1] - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - find: *collection_name - filter: - "encryptedDate": { - "$gt": { - "$binary": { - "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: find - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDate": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedDate": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Update.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Update.yml deleted file mode 100644 index 2b8b980b69..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Update.yml +++ /dev/null @@ -1,253 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$date': {'$numberLong': '0'}}, 'max': {'$date': {'$numberLong': '200'}}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Date. Update." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDate: { $date: { $numberLong: "0" } }} - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDate: { $date: { $numberLong: "1" } }} - - name: updateOne - arguments: - filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" } } }} - update: { "$set": { "encryptedDate": { $date: { $numberLong: "2" } }}} - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command_name: update - command: - "update": "default" - "ordered": true - "updates": [ - { - "q": { - "encryptedDate": { - "$gt": { - "$binary": { - "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - }, - "u": { - "$set": { - "encryptedDate": { $$type: "binData" } - } - } - } - ] - encryptionInformation: - type: 1 - schema: - "default.default": - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - "$db": "default" - - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDate": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedDate": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Aggregate.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Aggregate.yml deleted file mode 100644 index f76ce3e0ef..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Aggregate.yml +++ /dev/null @@ -1,1688 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. - topology: [ "replicaset" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalNoPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Decimal. Aggregate." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } - - name: aggregate - arguments: - pipeline: [{ $match: { "encryptedDecimalNoPrecision": { $gt: {$numberDecimal: "0" }} } }] - result: [*doc1] - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - aggregate: *collection_name - pipeline: [ - { - "$match": { - "encryptedDecimalNoPrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - } - } - ] - cursor: {} - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: aggregate - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": { - "$numberInt": "0" - }, - "encryptedDecimalNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", - "subType": "00" - } - } - ] - } - - - { - "_id": { - "$numberInt": "1" - }, - "encryptedDecimalNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RGTjNVEsNJb+DG7DpPOam8rQWD5HZAMpRyiTQaw7tk8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RlQWwhU+uVv0a+9IB5cUkEfvHBvOw3B1Sx6WfPWMqes=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ubb81XTC7U+4tcNzf1oYvOY6gR5hC2Izqx54f4GuJ0E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6M4Q5NMQ9TqNnjzGOxIkiUIY8TEL0I3XD1QnhefQUqU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BtInzk9t2FFMCEY6AQ7zN8jwrrZEs2irSv6q0Q4NaIw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6vxXfETu9cuBIpRBo3jUUU04mJIH/aAhLX8K6VI5Xv0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wXPCdS+q23zi1bkPnaVG2j0PsVtxdeSLJ//h6J1x8RU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KY3KkfBAsN2l80wbpj41G0gwBR5KmmFnZcagg7D3ENk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tI8NFAxXCX4VOnY5X73K6KI/Yspd3aR94KV39MhJlAw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "nFxH0UC3mATKA6Vboz+QX/hAjj19kF/SH6H5Cne7qC0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q8hYqIYaIi7nOdG/7qQZYnz8Bsacfi66M1nVku4SH08=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4saA92R4arp4anvD9xFtze+sNcQqTEhPHyl1h70A8NE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DbIziOBRRyeQS6RtBR09E37LV+CTKrEjGoRMLSpG6eE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Fv80Plp/7w2gnVqrwawLd6qhJ10G4NCDm3re67cNq4Y=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "T/T2oiQCBBES4YN7EodzPRdabZSFlYIClHBym+bQUZE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ZQgHD3l46Ujqtbnj1VbbeM29C9wJzOhz+yZ/7XdSrxk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ltlFKzWvyZvHxDFOYDd/XXJ6kUiJj0ln2HTCEz2o4Z4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "flW8A7bltC1u8bzx0WJtxosGJdOVsJFfbx33jxnpFGg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SXO+92QbMKwUSG2t27ciunV1c3VvFkUuDmSczpRe008=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+KioGs1GM+xRBzFE67ePTWj04KMSE5/Y6qUF7nJ5kvU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L3xNVbh6YH+RzqABN+5Jgb7T234Efpn766DmUvxIxgg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hPF+60mBYPjh21dEmPlBhKgyc9S2qLtTkypYvnqP2Fc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EletRsETy2HcjaPIm2c8CkT7ch/P3pJJDC8hasepcSU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "r5bMXUaNKqLPxZ+TG9HYTG4aSDgcpim27rN8rQFkM0w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0Q7Erdr8+/S0wUEDDIqlS5XjBVWvhZY65K0uUDb6+Ns=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xEcnhXy35hbXNVBPOOt3TUHbxvKfQ48KjA9b6/rbMqQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "T8bEpiQNgsEudXvyKE9SZlSvbpV/LUaslsdqgSFltyo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hIoiaF2YjnxDbODfhFEB+JGZ5nf8suD3Shck5bwQ3N0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qnA6qzejeRJ0rsZaZ0zOvKAaXyxt5lpscKQNYFZNl4k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "anAKCL2DN/le2VaP0n2ucYSEH/DaaEH/8Sa4OqTZsRA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JCZlBJaFm618oWYSnT9Jr1MtwFVw4BZjOzO+5yWgR90=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yxyk4n9762WzcDVGnTn4jCqUnSMIVCrLDIjCX1QVj34=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fDI6fdKvDJwim5/CQwWZEzcrXE3LHgy7FTtffcC7tXE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Vex+gcz5T+WkzsVZQrkqUR2ryyZbnaOGuWpYvjN0zCw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8TLEXz+Gbbp6llHpZXVjLsdlYY9f6hrKpHVpyfDe0RY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7fTyt5BrunypS65TfOzFW2E2qdIuT4SLeDeGlbQoJCs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8fKGrkqN0/KuSjyXgDBmRauDKrSa//JBKRWHEB9xBf4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "s4codmG7uN4ss6P357jL21lazEe90M9GOK5WrOknSV0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RkSpua8XF+NUdxVDU90EbLUTTyZFX3tt3atBTroFaRk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "LnTCuCDyAHK5B9KXzjtwGmWB+qergQk2OCjnIx9MI2A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cBFh0virAX4pVXf/udIGI2951i0+0aZAdJcBVGtYnT4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "G54X6myQXWZ5fw/G31en3QbdgfXzL9+hFTtJpnWMqDI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EdsiiuezcsFJFnYIyGjCOhnqMj1BOwTB5EFxN+ERUkg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dVH9MXLtk0WTwGQ3xmrhOqfropMUkDW3o6paNPGl3NU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sB3HqXKWY3pKbuEH8BTbfNIGfbY+7/ZbOc3XC+JRNNI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WHyDk62Xhqbo4/iie2aLIM4x2uuAjv6102dJSHI58oM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pNUFuHpeNRDUZ/NrtII2c6sNc9eGR1lIUlIyXKERA+0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UPa+pdCqnN0bfAptdzldQOSd01gidrDKy8KhWrpSKAI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "l+7dOAlo+HUffMqFYXL6pgUFeTbwOM9CjKQLxEoLtc4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SRnDXV/rN6C8xwMutv9E1luv3DOUio3VkgPr8Cpm7Ew=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QcH6gl+gX7xZ7OWhUNQMbndJy0Piz49pDo6RsnLkVSA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "t+uL4DnfsI/Zll/KXWW1cOKX3Hu8WIkm3pt9efCVSAQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "myutHDctku/+Uug/nD8gRbYvmx/IovtoAAC2/fz2oHA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6C+cjD0e0nSCP6cPqQYbNG7SlOd6Mfvi8hyfm7Ng+D8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zg01JSoOj9oBKT0S1ldJucXzY5AKgreS+h2xJreWTOs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7qQ80/FjodHl1m1py/Oii0/9C/xWbLdhaRXQ+kkCP10=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YwWMNH07vL6c5Nhg+MRnVByhzUunu8y0VLM9z/XvR5U=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Dle8bU98+fudAbc14SToZFkwvV3tcYVsjDug0NWljpc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "J+eKL1vPJmlzltvhI6Li5Fz/TJmi3Ng+ehRTcs46API=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zB3XzfFygLwC3WHkj0up+VbEd25KKoce1vOpG/5bwK4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vnVnmOnL+z2pqwE+A6cVKS0Iwy4F4/2IiElJca9bUQM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+lG5r/Fpqry3BtFuvY67+RntmHAMDoLVOSGc6ZoXPb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L5MXQertqc6uj7ADe8aWKbd1sYHPCE7P1VYVg9Zc3VI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "imKONuZgopt0bhM3GMX2WVPwQYMTobuUUEdhcLfHs4c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "eOkU1J1uVbiVFWBerbXsSIVcF2nqiicTkFy4x7kFHB8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gI0uDhXeoH/UatDQKEf4qo8FHzWZDhb/wuWTqbq/ID4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cOkd5Aa3btYhtojE/smsF/PJnULqQ4NNqTkU6KXTFmo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "AWNJMs1MTe294oFipp8Y6P0CjpkZ4qCZoClQF3XcHq8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6gJtlzXOFhGYrVbTuRMmvMlDTwXdNtR9aGBlHZPwIMw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "LEmwVGA/xsEG7UrcOoYLFu6KCXgijzFznenknuDacm8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "mIRFPTXRrGaPtp/Ydij2jgkRe4uoUvAKxW2d8b9zYL0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "B+Uv2u48WALOO0L311z+eryjYQzKJVMfdHMZPhOAFmY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "INXXp0wDyVCq+NtfIrrC2ciETmyW/dWB/48/u4yLEZ4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "se7DGo8XrlrQDLEcco1tZrQt9kDe+0RTyl2bw/quG4w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vr0m2+Zk9lbN6UgWCyn8xJWJOokU3IDYab5U5q1+CgQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XI+eJ8Gy2JktG1gICgoj1qpsfy1tKmH0kglWbaQH6DA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A+UCuNnuAUqnQzspA6TVqUPRmtZmpSex5HFw7THRxs0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xaH2Ehfljd19uo0Fvb3iwkdaiWEVQd2YPoitgEPkhSM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "S/iZBJGcc8+qZxyMtab65MMBoSglybwk3x58Nb86gnY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "w14ZE5qqY5YgkS4Zcs9YNbrQbY1XfGOOHNn9bOYnFVQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0MhGd/jEF1vjkKGp+ZMn9SjLK54jkp9W4Hg+Sp/oxaI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "92QZ73e/NRTYgCm4aifaKth6aAsKnLLccBc0zx/qUTY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WOjzemCgFJOiGIp81RSVh/tFlzSTj9eFWcBnsiv2Ycs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DrsP9CmfKPjw5yLL8bnSeAxfNzAwlb+Z8OqCiKgBY7o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lMogqg8veBv6mri3/drMe9afJiKMvevkmGcw9BedfLo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "TxqwNcY8Tg2MPpNdkPBwvfpuTttSYRHU26DGECKYQ9o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "l0u1b4b4vYACWIwfnB7PZac4oDEgjQZCzHruNPTgAIY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "iVSGQ+cCfhbWIrY/v/WBORK92elu9gfRKyGhr6r/k00=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yK1forG50diEXte8ECzjfpHeYsPyuQ/dgxbxn/nzY5k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gIfTLCD3VwnOwkC0zPXWTqaITxX6ZplA69PO2a6zolc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "O/Zxlgh3WqpzJ7+Sd8XWMVID4/GXJUUWaSqfgDUi3b0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ZQ6yv368zwahUqSUYH/StL0Qgz/TwS1CzlMjVDvCciI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "m2rPEYkjwyiKdonMrKlcF7hya4lFOAUwEePJ3SgrNx8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Mq0yl5iVKlq71bT/dT/fXOWf2n90bTnXFnOdGDN0JOc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6qDGMXipPLC2O6EAAMjO2F9xx4rdqZso4IkPpH2304U=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jvQHRQQa2RIszE2LX2Hv2LbRhYawJ6qmtRt8HZzFQXg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ovJXQrkZlpeHRciKyE/WWNm5O389gRgzx1W+Dw596X4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "a4kgRNvYctGYqyQv9qScL/WkljTYVylJ9pE9KDULlxU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qV4Q48vPiCJMTjljotzYKI/zfExWpkKOSHGcAjGyDig=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jtI7zbBF+QW/aYYTkn90zzyHLXLgmy7l1bzgMb2oqic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q0KmJl9txPdn962UNvnfe6UFhdk9YaFZuTm33F+csso=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ULNdEqeZJgtmNOhN/Y9INzsE9AnxWYwOMn+pIbRXIFs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "R4oz9+wkdjpKe5tE1jpG7IURAnfvS5fLP4LrD5cZfTE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qG5Z7VhwSu/HT/YFTgDzyAAzJKq51xPw2HeEV5btYC4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OM/1DmIIZ5Qyhtq8TGkHTBEMVKjAnKRZMRXYtTG8ctc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2R5vZbljLXnDFA99YfGuRB7pAdPJVKsT25zLNMC0fUk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OMbavF2EmdAz1fHkLV3ctFEUDfriKhoT2gidwHZ9z1o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MWT4Zrw3/vVvTYMa1Is5Pjr3wEwnBfnEAPPUAHKQhNU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tBkRPfG9yxfKocQx5pAJX0oEHKPL0Tgtr+0UYe09InE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lqxpnDR/H0YgH7RcfKoNoaaRhe1SIazIeMbQ1fu9y3Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "utT1UdR22PWOTrOkZauztX613lAplV4eh/ejTRb7ZSk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "S+Y2yFyKi/a6FXhih4yGo29X8I8OT6/zwEoX6NMKT4o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QSjVppg29x6oS5yBg8OFjrFt0tuTpWCuKxfIy0k8YnE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "y3r6/Xsfvsl3HksXlVYkJgHUqpQGfICxg3x9f8Zw1qM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BSltHzEwDjFN4du9rDHAPvl22atlcTioEtt+gC5L1tk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0arGXjSN0006UnXbrWsGqhvBair569DeFDUME3Df3rA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "s/DumaMad08S+PBUUcrS+v42K0z8HgcdiQtrFAEu2Qs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EzJ8Y8N0OQBTlnvrK82PdevDNZZO4E6CNgYVu8Cj6Ks=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VA4vr8jBPI5QdiPrULzzZjBMIUbG3V7Slg5zm0bFcKc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YAOvEB2ZLtq9LQiFViBHWaxxWVVonC2rNYj9tN9s3L0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hgaHMo9aAGS+nBwvqnTjZO+YkiQPY1c1XcIYeaYKHyI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YvaoLt3ZpH0atB0tNzwMjpoxRYJXl0DqSjisMJiGVBE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EMmW6CptFsiLoPOi5/uAJQ2FmeLg6mCpuVLLrRWk7Mc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1jQsNMarSnarlYmXEuoFokeBMg/090qUD9wqo1Zn8Gs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hupXNKhRpJxpyDAAP1TgJ5JMZh9lhbMk6s7D7dMS3C8=", - "subType": "00" - } - } - ] - } - \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Correctness.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Correctness.yml deleted file mode 100644 index 088669787f..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Correctness.yml +++ /dev/null @@ -1,294 +0,0 @@ -# Test correctness results. -# Does not include command monitoring expectations or outcome assertions to make tests more readable. - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. - topology: [ "replicaset" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalNoPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "Find with $gt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0.0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1.0" } } - - name: find - arguments: - filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0.0" } }} - result: [*doc1] - - - description: "Find with $gte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalNoPrecision: { $gte: { $numberDecimal: "0.0" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $gt with no results" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "1.0" } }} - result: [] - - - description: "Find with $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalNoPrecision: { $lt: { $numberDecimal: "1.0" } }} - result: [*doc0] - - - description: "Find with $lte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalNoPrecision: { $lte: { $numberDecimal: "1.0" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $gt and $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} - result: [*doc1] - - - description: "Find with equality" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalNoPrecision: { $numberDecimal: "0.0" } } - result: [*doc0] - - name: find - arguments: - filter: { encryptedDecimalNoPrecision: { $numberDecimal: "1.0" } } - result: [*doc1] - - - description: "Find with $in" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalNoPrecision: { $in: [ {$numberDecimal: "0.0"} ] } } - result: [*doc0] - - - description: "Aggregate with $gte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalNoPrecision: { $gte: { $numberDecimal: "0.0" } }} } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $gt with no results" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "1.0" } }} } - result: [] - - - description: "Aggregate with $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalNoPrecision: { $lt: { $numberDecimal: "1.0" } }} } - result: [*doc0] - - - description: "Aggregate with $lte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalNoPrecision: { $lte: { $numberDecimal: "1.0" } }} } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $gt and $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} } - result: [*doc1] - - - description: "Aggregate with equality" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalNoPrecision: { $numberDecimal: "0.0" } } } - result: [*doc0] - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalNoPrecision: { $numberDecimal: "1.0" } } } - result: [*doc1] - - - description: "Aggregate with $in" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalNoPrecision: { $in: [ {$numberDecimal: "0.0"} ] } } } - result: [*doc0] - - - description: "Wrong type: Insert Int" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: { _id: 0, encryptedDecimalNoPrecision: { $numberInt: "0" }} } - result: - # Expect an error from mongocryptd. - errorContains: "cannot encrypt element" - - - description: "Wrong type: Find Int" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: find - arguments: - filter: { encryptedDecimalNoPrecision: { $gte: { $numberInt: "0" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: - # expect an error from libmongocrypt. - errorContains: "field type is not supported" \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Delete.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Delete.yml deleted file mode 100644 index c033f43e65..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Delete.yml +++ /dev/null @@ -1,906 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. - topology: [ "replicaset" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalNoPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Decimal. Delete." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } - - name: deleteOne - arguments: - filter: { "encryptedDecimalNoPrecision": { $gt: {$numberDecimal: "0" }} } - result: - deletedCount: 1 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - delete: *collection_name - deletes: [ - { - "q": { - "encryptedDecimalNoPrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - }, - "limit": 1 - } - ] - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: delete - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": { - "$numberInt": "0" - }, - "encryptedDecimalNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-FindOneAndUpdate.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-FindOneAndUpdate.yml deleted file mode 100644 index 2e162d1f64..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-FindOneAndUpdate.yml +++ /dev/null @@ -1,1685 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. - topology: [ "replicaset" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalNoPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Decimal. FindOneAndUpdate." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } - - name: findOneAndUpdate - arguments: - filter: { encryptedDecimalNoPrecision: { $gt: {$numberDecimal: "0"}} } - update: { "$set": { "encryptedDecimalNoPrecision": {$numberDecimal: "2"}}} - returnDocument: Before - result: *doc1 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - findAndModify: *collection_name - query: { - "encryptedDecimalNoPrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - } - update: { "$set": {"encryptedDecimalNoPrecision": { $$type: "binData" }} } - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: findAndModify - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": { - "$numberInt": "0" - }, - "encryptedDecimalNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", - "subType": "00" - } - } - ] - } - - - { - "_id": { - "$numberInt": "1" - }, - "encryptedDecimalNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Mr/laWHUijZT5VT3x2a7crb7wgd/UXOGz8jr8BVqBpM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VDCpBYsJIxTfcI6Zgf7FTmKMxUffQv+Ys8zt5dlK76I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zYDslUwOUVNwTYkETfjceH/PU3bac9X3UuQyYJ19qK0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rAOmHSz18Jx107xpbv9fYcPOmh/KPAqge0PAtuhIRnc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BFOB1OGVUen7VsOuS0g8Ti7oDsTt2Yj/k/7ta8YAdGM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2fckE5SPs0GU+akDkUEM6mm0EtcV3WDE/sQsnTtodlk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "mi9+aNjuwIvaMpSHENvKzKRAmX9cYguo2mXLvOoftHQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "K6TWn4VcWWkz/gkUkLmbtwkG7SNeABICmLDnoYJFlLU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Z+2/cEtGU0Fq7QJFNGA/0y4aWAsw0ncG6X0LYRqwS3c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rrSIf+lgcNZFbbUkS9BmE045jRWBpcBJXHzfMVEFuzE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KlHL3Kyje1/LMIfgbCqw1SolxffJvvgsYBV5y77wxuA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hzJ1YBoETmYeCh352dBmG8d8Wse/bUcqojTWpWQlgsc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lSdcllDXx8MA+s0GULjDA1lQkcV0L8/aHtZ6dM2pZ2c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "HGr7JLTTA7ksAnlmjSIwwdBVvgr3fv46/FTdiCPYpos=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "mMr25v1VwOEVZ8xaNUTHJCcsYqV+kwK6RzGYilxPtJ4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "129hJbziPJzNo0IoTU3bECdge0FtaPW8dm4dyNVNwYU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "doiLJ96qoo+v7NqIAZLq6BI5axV8Id8gT5vyJ1ZZ0PM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cW/Lcul3xYmfyvI/0x/+ybN78aQmBK1XIGs1EEU09N8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1aVIwzu9N5EJV9yEES+/g6hOTH7cA2NTcLIc59cu0wU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kw5tyl7Ew0r1wFyrN1mB9FiVW2hK2BxxxUuJDNWjyjQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ADAY2YBrm6RJBDY/eLLcfNxmSJku+mefz74gH66oyco=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8gkqB1LojzPrstpFG7RHYmWxXpIlPDTqWnNsXH7XDRU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "TESfVQMDQjfTZmHmUeYUE2XrokJ6CcrsKx/GmypGjOw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qFM+HFVQ539S0Ouynd1fBHoemFxtU9PRxE5+Dq7Ljy4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jPiFgUZteSmOg4wf3bsEKCZzcnxmMoILsgp/GaZD+dM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YaWUgJhYgPNN7TkFK16H8SsQS226JguaVhOIQxZwQNQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x90/Qk3AgyaFsvWf2KUCu5XF3j76WFSjt/GrnG01060=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ZGWybWL/xlEdMYRFCZDUoz10sywTf7U/7wufsb78lH0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8l4ganN66jIcdxfHAdYLaym/mdzUUQ8TViw3MDRySPc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "c8p5XEGTqxqvRGVlR+nkxw9uUdoqDqTB0jlYQ361qMA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1ZGFLlpQBcU3zIUg8MmgWwFKVz/SaA7eSYFrfe3Hb70=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "34529174M77rHr3Ftn9r8jU4a5ztYtyVhMn1wryZSkU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YkQ4pxFWzc49MS0vZM6S8mNo4wAwo21rePBeF3C+9mI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MhOf4mYY00KKVhptOcXf0bXB7WfuuM801MRJg4vXPgc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7pbbD8ihNIYIBJ3tAUPGzHpFPpIeCTAk5L88qCB0/9w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "C9Q5PoNJTQo6pmNzXEEXUEqH22//UUWY1gqILcIywec=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "AqGVk1QjDNDLYWGRBX/nv9QdGR2SEgXZEhF0EWBAiSE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/sGI3VCbJUKATULJmhTayPOeVW+5MjWSvVCqS77sRbU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yOtbL0ih7gsuoxVtRrACMz+4N5uo7jIR7zzmtih2Beo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uA6dkb2Iyg9Su8UNDvZzkPx33kPZtWr/CCuEY+XgzUM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1DoSFPdHIplqZk+DyWAmEPckWwXw/GdB25NLmzeEZhk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OfDVS0T3ZuIXI/LNbTp6C9UbPIWLKiMy6Wx+9tqNl+g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "3PZjHXbmG6GtPz+iapKtQ3yY4PoFFgjIy+fV2xQv1YU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kaoLN0BoBWsmqE7kKkJQejATmLShd8qffcAmlhsxsGY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vpiw9KgQdegGmp7IJnSGX2miujRLU0xzs0ITTqbPW7c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "NuXFf7xGUefYjIUTuMxNUTCfVHrF8oL0AT7dPv5Plk4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8Tz53LxtfEBJ9eR+d2690kwNsqPV6XyKo2PlqZCbUrc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "e6zsOmHSyV8tyQtSX6BSwui6wK9v1xG3giY/IILJQ2w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2fedFMCxa2DzmIpfbDKGXhQg0PPwbUv6vIWdwwlvhms=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yEJKMFnWXTC8tJUfzCInzQRByNEPjHxpw4L4m8No91Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YbFuWwOiFuQyOzIJXDbOkCWC2DyrG+248TBuVCa1pXU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "w7IkwGdrguwDrar5+w0Z3va5wXyZ4VXJkDMISyRjPGo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YmJUoILTRJPhyIyWyXJTsQ6KSZHHbEpwPVup6Ldm/Ko=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FvMjcwVZJmfh6FP/yBg2wgskK+KHD8YVUY6WtrE8xbg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "h4HCtD4HyYz0nci49IVAa10Z4NJD/FHnRMV4sRX6qro=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "nC7BpXCmym+a0Is2kReM9cYN2M1Eh5rVo8fjms14Oiw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1qtVWaeVo649ZZZtN8gXbwLgMWGLhz8beODbvru0I7Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Ej+mC0QFyMNIiSjR939S+iGBm7dm+1xObu5IcF/OpbU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UQ8LbUG3cMegbr9yKfKanAPQE1EfPkFciVDrNqZ5GHY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4iI3mXIDjnX+ralk1HhJY43mZx2uTJM7hsv9MQzTX7E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0WQCcs3rvsasgohERHHCaBM4Iy6yomS4qJ5To3/yYiw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qDCTVPoue1/DOAGNAlUstdA9Sid8MgEY4e5EzHcVHRk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9F9Mus0UnlzHb8E8ImxgXtz6SU98YXD0JqswOKw/Bzs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pctHpHKVBBcsahQ6TNh6/1V1ZrqOtKSAPtATV6BJqh0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vfR3C/4cPkVdxtNaqtF/v635ONbhTf5WbwJM6s4EXNE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ejP43xUBIex6szDcqExAFpx1IE/Ksi5ywJ84GKDFRrs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jbP4AWYd3S2f3ejmMG7dS5IbrFol48UUoT+ve3JLN6U=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CiDifI7958sUjNqJUBQULeyF7x0Up3loPWvYKw9uAuw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "e2dQFsiHqd2BFHNhlSxocjd+cPs4wkcUW/CnCz4KNuM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PJFckVmzBipqaEqsuP2mkjhJE4qhw36NhfQ9DcOHyEU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "S3MeuJhET/B8VcfZYDR9fvX0nscDj416jdDekhmK11s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CGVHZRXpuNtQviDB2Kj03Q8uvs4w3RwTgV847R7GwPw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yUGgmgyLrxbEpDVy89XN3c2cmFpZXWWmuJ/35zVZ+Jw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "inb6Q97mL1a9onfNTT8v9wsoi/fz7KXKq3p8j90AU9c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CCyYx/4npq9xGO1lsCo8ZJhFO9/tN7DB+/DTE778rYg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "LNnYw4fwbiAZu0kBdAHPEm/OFnreS+oArdB5O/l/I98=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "P006SxmUS/RjiQJVYPdMFnNo3827GIEmSzagggkg05Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "oyvwY+WsnYV6UHuPki1o0ILJ2jN4uyXf9yaUNtZJyBA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "36Lk3RHWh1wmtCWC/Yj6jNIo17U5y6SofAgQjzjVxD8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vOOo8FqeHnuO9mqOYjIb4vgwIwVyXZ5Y+bY5d9tGFUM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "bJiDJjwQRNxqxlGjRm5lLziFhcfTDCnQ/qU1V85qcRg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2Qgrm1n0wUELAQnpkEiIHB856yv76q8jLbpiucetcm0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "5ciPOYxTK0WDwwYyfs7yiVymwtYQXDELLxmM4JLl4/o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "31dC2WUSIOKQc4jwT6PikfeYTwi80mTlh7P31T5KNQU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YluTV2Mu53EGCKLcWfHZb0BM/IPW2xJdG3vYlDMEsM4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dh/8lGo2Ek6KukSwutH6Q35iy8TgV0FN0SJqe0ZVHN8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EVw6HpIs3BKen2qY2gz4y5dw1JpXilfh07msZfQqJpc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FYolLla9L8EZMROEdWetozroU40Dnmwwx2jIMrr7c1A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8M6k4QIutSIj6CM41vvkQtuFsaGrjoR9SZJVSLbfGKQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9LM0VoddDNHway442MqY+Z7vohB2UHau/cddshhzf40=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "66i8Ytco4Yq/FMl6pIRZazz3CZlu8fO2OI6Pne0pvHU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2a/HgX+MjZxjXtSvHgF1yEpHMJBkl8Caee8XrJtn0WM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "frhBM662c4ZVG7mWP8K/HhRjd01lydW/cPcHnDjifqc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6k1T7Q1t668PBqv6fwpVnT1HWh7Am5LtbKvwPJKcpGU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UlJ5Edfusp8S/Pyhw6KTglIejmbr1HO0zUeHn/qFETA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jsxsB+1ECB3assUdoC333do9tYH+LglHmVSJHy4N8Hg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2nzIQxGYF7j3bGsIesECEOqhObKs/9ywknPHeJ3yges=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xJYKtuWrX90JrJVoYtnwP7Ce59XQGFYoalxpNfBXEH0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "NLI5lriBTleGCELcHBtNnmnvwSRkHHaLOX4cKboMgTw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hUOQV0RmE5aJdJww1AR9rirJG4zOYPo+6cCkgn/BGvQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "h4G2Of76AgxcUziBwCyH+ayMOpdBWzg4yFrTfehSC2c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VuamM75RzGfQpj2/Y1jSVuQLrhy6OAwlZxjuQLB/9Ss=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kn9+hLq7hvw02xr9vrplOCDXKBTuFhfbX7d5v/l85Pg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fAiGqKyLZpGngBYFbtYUYt8LUrJ49vYafiboifTDjxs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BxRILymgfVJCczqjUIWXcfrfSgrrYkxTM5VTg0HkZLY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CrFY/PzfPU2zsFkGLu/dI6mEeizZzCR+uYgjZBAHro0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "AEbrIuwvXLTtYgMjOqnGQ8y8axUn5Ukrn7UZRSyfQVw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ouWeVH3PEFg+dKWlXc6BmqirJOaVWjJbMzZbCsce4dA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+hd6xFB+EG+kVP7WH4uMd1CLaWMnt5xJRaY/Guuga9Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zmpGalfAOL3gmcUMJYcLYIRT/2VDO/1Dw4KdYZoNcng=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2PbHAoM/46J2UIZ/vyksKzmVVfxA7YUyIxWeL/N/vBk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7fD9x+zk5MVFesb59Klqiwwmve7P5ON/5COURXj5smE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tlrNQ4jaq051iaWonuv1sSrYhKkL1LtNZuHsvATha3s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fBodm28iClNpvlRyVq0dOdXQ08S7/N3aDwid+PdWvRo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "O+/nnRqT3Zv7yMMGug8GhKHaWy6u7BfRGtZoj0sdN1c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "5AZZ/RTMY4Photnm/cpXZr/HnFRi3eljacMsipkJLHA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "oFVyo/kgoMxBIk2VE52ySSimeyU+Gr0EfCwapXnTpKA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Z8v59DfcnviA0mzvnUk+URVO0UuqAWvtarEgJva/n1c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "P64GOntZ+zBJEHkigoh9FSxSO+rJTqR20z5aiGQ9an4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xMbSuDPfWuO/Dm7wuVl06GnzG9uzTlJJX9vFy7boGlY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kXPB19mRClxdH2UsHwlttS6lLU2uHvzuZgZz7kC45jU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "NDVjVYXAw4k0w4tFzvs7QDq39aaU3HQor4I2XMKKnCk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uKw/+ErVfpTO1dGUfd3T/eWfZW3nUxXCdBGdjvHtZ88=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "av0uxEzWkizYWm0QUM/MN1hLibnxPvCWJKwjOV4yVQY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ERwUC47dvgOBzIsEESMIioLYbFOxOe8PtJTnmDkKuHM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2gseKlG5Le12fS/vj4eaED4lturF16kAgJ1TpW3HxEE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7Cvg0Y3j/5i2F1TeXxlMmU7xwif5dCmwkZAOrVC5K2Y=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-InsertFind.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-InsertFind.yml deleted file mode 100644 index d9720d6722..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-InsertFind.yml +++ /dev/null @@ -1,1681 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. - topology: [ "replicaset" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalNoPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Decimal. Insert and Find." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } - - name: find - arguments: - filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0" } } } - result: [*doc1] - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - find: *collection_name - filter: - "encryptedDecimalNoPrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: find - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": { - "$numberInt": "0" - }, - "encryptedDecimalNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", - "subType": "00" - } - } - ] - } - - - { - "_id": { - "$numberInt": "1" - }, - "encryptedDecimalNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RGTjNVEsNJb+DG7DpPOam8rQWD5HZAMpRyiTQaw7tk8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RlQWwhU+uVv0a+9IB5cUkEfvHBvOw3B1Sx6WfPWMqes=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ubb81XTC7U+4tcNzf1oYvOY6gR5hC2Izqx54f4GuJ0E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6M4Q5NMQ9TqNnjzGOxIkiUIY8TEL0I3XD1QnhefQUqU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BtInzk9t2FFMCEY6AQ7zN8jwrrZEs2irSv6q0Q4NaIw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6vxXfETu9cuBIpRBo3jUUU04mJIH/aAhLX8K6VI5Xv0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wXPCdS+q23zi1bkPnaVG2j0PsVtxdeSLJ//h6J1x8RU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KY3KkfBAsN2l80wbpj41G0gwBR5KmmFnZcagg7D3ENk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tI8NFAxXCX4VOnY5X73K6KI/Yspd3aR94KV39MhJlAw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "nFxH0UC3mATKA6Vboz+QX/hAjj19kF/SH6H5Cne7qC0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q8hYqIYaIi7nOdG/7qQZYnz8Bsacfi66M1nVku4SH08=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4saA92R4arp4anvD9xFtze+sNcQqTEhPHyl1h70A8NE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DbIziOBRRyeQS6RtBR09E37LV+CTKrEjGoRMLSpG6eE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Fv80Plp/7w2gnVqrwawLd6qhJ10G4NCDm3re67cNq4Y=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "T/T2oiQCBBES4YN7EodzPRdabZSFlYIClHBym+bQUZE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ZQgHD3l46Ujqtbnj1VbbeM29C9wJzOhz+yZ/7XdSrxk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ltlFKzWvyZvHxDFOYDd/XXJ6kUiJj0ln2HTCEz2o4Z4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "flW8A7bltC1u8bzx0WJtxosGJdOVsJFfbx33jxnpFGg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SXO+92QbMKwUSG2t27ciunV1c3VvFkUuDmSczpRe008=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+KioGs1GM+xRBzFE67ePTWj04KMSE5/Y6qUF7nJ5kvU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L3xNVbh6YH+RzqABN+5Jgb7T234Efpn766DmUvxIxgg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hPF+60mBYPjh21dEmPlBhKgyc9S2qLtTkypYvnqP2Fc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EletRsETy2HcjaPIm2c8CkT7ch/P3pJJDC8hasepcSU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "r5bMXUaNKqLPxZ+TG9HYTG4aSDgcpim27rN8rQFkM0w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0Q7Erdr8+/S0wUEDDIqlS5XjBVWvhZY65K0uUDb6+Ns=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xEcnhXy35hbXNVBPOOt3TUHbxvKfQ48KjA9b6/rbMqQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "T8bEpiQNgsEudXvyKE9SZlSvbpV/LUaslsdqgSFltyo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hIoiaF2YjnxDbODfhFEB+JGZ5nf8suD3Shck5bwQ3N0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qnA6qzejeRJ0rsZaZ0zOvKAaXyxt5lpscKQNYFZNl4k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "anAKCL2DN/le2VaP0n2ucYSEH/DaaEH/8Sa4OqTZsRA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JCZlBJaFm618oWYSnT9Jr1MtwFVw4BZjOzO+5yWgR90=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yxyk4n9762WzcDVGnTn4jCqUnSMIVCrLDIjCX1QVj34=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fDI6fdKvDJwim5/CQwWZEzcrXE3LHgy7FTtffcC7tXE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Vex+gcz5T+WkzsVZQrkqUR2ryyZbnaOGuWpYvjN0zCw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8TLEXz+Gbbp6llHpZXVjLsdlYY9f6hrKpHVpyfDe0RY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7fTyt5BrunypS65TfOzFW2E2qdIuT4SLeDeGlbQoJCs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8fKGrkqN0/KuSjyXgDBmRauDKrSa//JBKRWHEB9xBf4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "s4codmG7uN4ss6P357jL21lazEe90M9GOK5WrOknSV0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RkSpua8XF+NUdxVDU90EbLUTTyZFX3tt3atBTroFaRk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "LnTCuCDyAHK5B9KXzjtwGmWB+qergQk2OCjnIx9MI2A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cBFh0virAX4pVXf/udIGI2951i0+0aZAdJcBVGtYnT4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "G54X6myQXWZ5fw/G31en3QbdgfXzL9+hFTtJpnWMqDI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EdsiiuezcsFJFnYIyGjCOhnqMj1BOwTB5EFxN+ERUkg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dVH9MXLtk0WTwGQ3xmrhOqfropMUkDW3o6paNPGl3NU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sB3HqXKWY3pKbuEH8BTbfNIGfbY+7/ZbOc3XC+JRNNI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WHyDk62Xhqbo4/iie2aLIM4x2uuAjv6102dJSHI58oM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pNUFuHpeNRDUZ/NrtII2c6sNc9eGR1lIUlIyXKERA+0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UPa+pdCqnN0bfAptdzldQOSd01gidrDKy8KhWrpSKAI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "l+7dOAlo+HUffMqFYXL6pgUFeTbwOM9CjKQLxEoLtc4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SRnDXV/rN6C8xwMutv9E1luv3DOUio3VkgPr8Cpm7Ew=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QcH6gl+gX7xZ7OWhUNQMbndJy0Piz49pDo6RsnLkVSA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "t+uL4DnfsI/Zll/KXWW1cOKX3Hu8WIkm3pt9efCVSAQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "myutHDctku/+Uug/nD8gRbYvmx/IovtoAAC2/fz2oHA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6C+cjD0e0nSCP6cPqQYbNG7SlOd6Mfvi8hyfm7Ng+D8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zg01JSoOj9oBKT0S1ldJucXzY5AKgreS+h2xJreWTOs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7qQ80/FjodHl1m1py/Oii0/9C/xWbLdhaRXQ+kkCP10=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YwWMNH07vL6c5Nhg+MRnVByhzUunu8y0VLM9z/XvR5U=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Dle8bU98+fudAbc14SToZFkwvV3tcYVsjDug0NWljpc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "J+eKL1vPJmlzltvhI6Li5Fz/TJmi3Ng+ehRTcs46API=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zB3XzfFygLwC3WHkj0up+VbEd25KKoce1vOpG/5bwK4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vnVnmOnL+z2pqwE+A6cVKS0Iwy4F4/2IiElJca9bUQM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+lG5r/Fpqry3BtFuvY67+RntmHAMDoLVOSGc6ZoXPb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L5MXQertqc6uj7ADe8aWKbd1sYHPCE7P1VYVg9Zc3VI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "imKONuZgopt0bhM3GMX2WVPwQYMTobuUUEdhcLfHs4c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "eOkU1J1uVbiVFWBerbXsSIVcF2nqiicTkFy4x7kFHB8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gI0uDhXeoH/UatDQKEf4qo8FHzWZDhb/wuWTqbq/ID4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cOkd5Aa3btYhtojE/smsF/PJnULqQ4NNqTkU6KXTFmo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "AWNJMs1MTe294oFipp8Y6P0CjpkZ4qCZoClQF3XcHq8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6gJtlzXOFhGYrVbTuRMmvMlDTwXdNtR9aGBlHZPwIMw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "LEmwVGA/xsEG7UrcOoYLFu6KCXgijzFznenknuDacm8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "mIRFPTXRrGaPtp/Ydij2jgkRe4uoUvAKxW2d8b9zYL0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "B+Uv2u48WALOO0L311z+eryjYQzKJVMfdHMZPhOAFmY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "INXXp0wDyVCq+NtfIrrC2ciETmyW/dWB/48/u4yLEZ4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "se7DGo8XrlrQDLEcco1tZrQt9kDe+0RTyl2bw/quG4w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vr0m2+Zk9lbN6UgWCyn8xJWJOokU3IDYab5U5q1+CgQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XI+eJ8Gy2JktG1gICgoj1qpsfy1tKmH0kglWbaQH6DA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A+UCuNnuAUqnQzspA6TVqUPRmtZmpSex5HFw7THRxs0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xaH2Ehfljd19uo0Fvb3iwkdaiWEVQd2YPoitgEPkhSM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "S/iZBJGcc8+qZxyMtab65MMBoSglybwk3x58Nb86gnY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "w14ZE5qqY5YgkS4Zcs9YNbrQbY1XfGOOHNn9bOYnFVQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0MhGd/jEF1vjkKGp+ZMn9SjLK54jkp9W4Hg+Sp/oxaI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "92QZ73e/NRTYgCm4aifaKth6aAsKnLLccBc0zx/qUTY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WOjzemCgFJOiGIp81RSVh/tFlzSTj9eFWcBnsiv2Ycs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DrsP9CmfKPjw5yLL8bnSeAxfNzAwlb+Z8OqCiKgBY7o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lMogqg8veBv6mri3/drMe9afJiKMvevkmGcw9BedfLo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "TxqwNcY8Tg2MPpNdkPBwvfpuTttSYRHU26DGECKYQ9o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "l0u1b4b4vYACWIwfnB7PZac4oDEgjQZCzHruNPTgAIY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "iVSGQ+cCfhbWIrY/v/WBORK92elu9gfRKyGhr6r/k00=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yK1forG50diEXte8ECzjfpHeYsPyuQ/dgxbxn/nzY5k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gIfTLCD3VwnOwkC0zPXWTqaITxX6ZplA69PO2a6zolc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "O/Zxlgh3WqpzJ7+Sd8XWMVID4/GXJUUWaSqfgDUi3b0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ZQ6yv368zwahUqSUYH/StL0Qgz/TwS1CzlMjVDvCciI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "m2rPEYkjwyiKdonMrKlcF7hya4lFOAUwEePJ3SgrNx8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Mq0yl5iVKlq71bT/dT/fXOWf2n90bTnXFnOdGDN0JOc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6qDGMXipPLC2O6EAAMjO2F9xx4rdqZso4IkPpH2304U=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jvQHRQQa2RIszE2LX2Hv2LbRhYawJ6qmtRt8HZzFQXg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ovJXQrkZlpeHRciKyE/WWNm5O389gRgzx1W+Dw596X4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "a4kgRNvYctGYqyQv9qScL/WkljTYVylJ9pE9KDULlxU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qV4Q48vPiCJMTjljotzYKI/zfExWpkKOSHGcAjGyDig=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jtI7zbBF+QW/aYYTkn90zzyHLXLgmy7l1bzgMb2oqic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q0KmJl9txPdn962UNvnfe6UFhdk9YaFZuTm33F+csso=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ULNdEqeZJgtmNOhN/Y9INzsE9AnxWYwOMn+pIbRXIFs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "R4oz9+wkdjpKe5tE1jpG7IURAnfvS5fLP4LrD5cZfTE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qG5Z7VhwSu/HT/YFTgDzyAAzJKq51xPw2HeEV5btYC4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OM/1DmIIZ5Qyhtq8TGkHTBEMVKjAnKRZMRXYtTG8ctc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2R5vZbljLXnDFA99YfGuRB7pAdPJVKsT25zLNMC0fUk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OMbavF2EmdAz1fHkLV3ctFEUDfriKhoT2gidwHZ9z1o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MWT4Zrw3/vVvTYMa1Is5Pjr3wEwnBfnEAPPUAHKQhNU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tBkRPfG9yxfKocQx5pAJX0oEHKPL0Tgtr+0UYe09InE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lqxpnDR/H0YgH7RcfKoNoaaRhe1SIazIeMbQ1fu9y3Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "utT1UdR22PWOTrOkZauztX613lAplV4eh/ejTRb7ZSk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "S+Y2yFyKi/a6FXhih4yGo29X8I8OT6/zwEoX6NMKT4o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QSjVppg29x6oS5yBg8OFjrFt0tuTpWCuKxfIy0k8YnE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "y3r6/Xsfvsl3HksXlVYkJgHUqpQGfICxg3x9f8Zw1qM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BSltHzEwDjFN4du9rDHAPvl22atlcTioEtt+gC5L1tk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0arGXjSN0006UnXbrWsGqhvBair569DeFDUME3Df3rA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "s/DumaMad08S+PBUUcrS+v42K0z8HgcdiQtrFAEu2Qs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EzJ8Y8N0OQBTlnvrK82PdevDNZZO4E6CNgYVu8Cj6Ks=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VA4vr8jBPI5QdiPrULzzZjBMIUbG3V7Slg5zm0bFcKc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YAOvEB2ZLtq9LQiFViBHWaxxWVVonC2rNYj9tN9s3L0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hgaHMo9aAGS+nBwvqnTjZO+YkiQPY1c1XcIYeaYKHyI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YvaoLt3ZpH0atB0tNzwMjpoxRYJXl0DqSjisMJiGVBE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EMmW6CptFsiLoPOi5/uAJQ2FmeLg6mCpuVLLrRWk7Mc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1jQsNMarSnarlYmXEuoFokeBMg/090qUD9wqo1Zn8Gs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hupXNKhRpJxpyDAAP1TgJ5JMZh9lhbMk6s7D7dMS3C8=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Update.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Update.yml deleted file mode 100644 index dd2e8c0f79..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Update.yml +++ /dev/null @@ -1,1698 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. - topology: [ "replicaset" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalNoPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Decimal. Update." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } - - name: updateOne - arguments: - filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0" } } } - update: { "$set": { "encryptedDecimalNoPrecision": { $numberDecimal: "2" } }} - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command_name: update - command: - "update": "default" - "ordered": true - "updates": [ - { - "q": { - "encryptedDecimalNoPrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - }, - "u": { - "$set": { - "encryptedDecimalNoPrecision": { $$type: "binData" } - } - } - } - ] - encryptionInformation: - type: 1 - schema: - "default.default": - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - "$db": "default" - - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": { - "$numberInt": "0" - }, - "encryptedDecimalNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", - "subType": "00" - } - } - ] - } - - - { - "_id": { - "$numberInt": "1" - }, - "encryptedDecimalNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Mr/laWHUijZT5VT3x2a7crb7wgd/UXOGz8jr8BVqBpM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VDCpBYsJIxTfcI6Zgf7FTmKMxUffQv+Ys8zt5dlK76I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zYDslUwOUVNwTYkETfjceH/PU3bac9X3UuQyYJ19qK0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rAOmHSz18Jx107xpbv9fYcPOmh/KPAqge0PAtuhIRnc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BFOB1OGVUen7VsOuS0g8Ti7oDsTt2Yj/k/7ta8YAdGM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2fckE5SPs0GU+akDkUEM6mm0EtcV3WDE/sQsnTtodlk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "mi9+aNjuwIvaMpSHENvKzKRAmX9cYguo2mXLvOoftHQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "K6TWn4VcWWkz/gkUkLmbtwkG7SNeABICmLDnoYJFlLU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Z+2/cEtGU0Fq7QJFNGA/0y4aWAsw0ncG6X0LYRqwS3c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rrSIf+lgcNZFbbUkS9BmE045jRWBpcBJXHzfMVEFuzE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KlHL3Kyje1/LMIfgbCqw1SolxffJvvgsYBV5y77wxuA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hzJ1YBoETmYeCh352dBmG8d8Wse/bUcqojTWpWQlgsc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lSdcllDXx8MA+s0GULjDA1lQkcV0L8/aHtZ6dM2pZ2c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "HGr7JLTTA7ksAnlmjSIwwdBVvgr3fv46/FTdiCPYpos=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "mMr25v1VwOEVZ8xaNUTHJCcsYqV+kwK6RzGYilxPtJ4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "129hJbziPJzNo0IoTU3bECdge0FtaPW8dm4dyNVNwYU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "doiLJ96qoo+v7NqIAZLq6BI5axV8Id8gT5vyJ1ZZ0PM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cW/Lcul3xYmfyvI/0x/+ybN78aQmBK1XIGs1EEU09N8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1aVIwzu9N5EJV9yEES+/g6hOTH7cA2NTcLIc59cu0wU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kw5tyl7Ew0r1wFyrN1mB9FiVW2hK2BxxxUuJDNWjyjQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ADAY2YBrm6RJBDY/eLLcfNxmSJku+mefz74gH66oyco=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8gkqB1LojzPrstpFG7RHYmWxXpIlPDTqWnNsXH7XDRU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "TESfVQMDQjfTZmHmUeYUE2XrokJ6CcrsKx/GmypGjOw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qFM+HFVQ539S0Ouynd1fBHoemFxtU9PRxE5+Dq7Ljy4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jPiFgUZteSmOg4wf3bsEKCZzcnxmMoILsgp/GaZD+dM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YaWUgJhYgPNN7TkFK16H8SsQS226JguaVhOIQxZwQNQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x90/Qk3AgyaFsvWf2KUCu5XF3j76WFSjt/GrnG01060=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ZGWybWL/xlEdMYRFCZDUoz10sywTf7U/7wufsb78lH0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8l4ganN66jIcdxfHAdYLaym/mdzUUQ8TViw3MDRySPc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "c8p5XEGTqxqvRGVlR+nkxw9uUdoqDqTB0jlYQ361qMA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1ZGFLlpQBcU3zIUg8MmgWwFKVz/SaA7eSYFrfe3Hb70=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "34529174M77rHr3Ftn9r8jU4a5ztYtyVhMn1wryZSkU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YkQ4pxFWzc49MS0vZM6S8mNo4wAwo21rePBeF3C+9mI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MhOf4mYY00KKVhptOcXf0bXB7WfuuM801MRJg4vXPgc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7pbbD8ihNIYIBJ3tAUPGzHpFPpIeCTAk5L88qCB0/9w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "C9Q5PoNJTQo6pmNzXEEXUEqH22//UUWY1gqILcIywec=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "AqGVk1QjDNDLYWGRBX/nv9QdGR2SEgXZEhF0EWBAiSE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/sGI3VCbJUKATULJmhTayPOeVW+5MjWSvVCqS77sRbU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yOtbL0ih7gsuoxVtRrACMz+4N5uo7jIR7zzmtih2Beo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uA6dkb2Iyg9Su8UNDvZzkPx33kPZtWr/CCuEY+XgzUM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1DoSFPdHIplqZk+DyWAmEPckWwXw/GdB25NLmzeEZhk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OfDVS0T3ZuIXI/LNbTp6C9UbPIWLKiMy6Wx+9tqNl+g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "3PZjHXbmG6GtPz+iapKtQ3yY4PoFFgjIy+fV2xQv1YU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kaoLN0BoBWsmqE7kKkJQejATmLShd8qffcAmlhsxsGY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vpiw9KgQdegGmp7IJnSGX2miujRLU0xzs0ITTqbPW7c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "NuXFf7xGUefYjIUTuMxNUTCfVHrF8oL0AT7dPv5Plk4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8Tz53LxtfEBJ9eR+d2690kwNsqPV6XyKo2PlqZCbUrc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "e6zsOmHSyV8tyQtSX6BSwui6wK9v1xG3giY/IILJQ2w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2fedFMCxa2DzmIpfbDKGXhQg0PPwbUv6vIWdwwlvhms=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yEJKMFnWXTC8tJUfzCInzQRByNEPjHxpw4L4m8No91Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YbFuWwOiFuQyOzIJXDbOkCWC2DyrG+248TBuVCa1pXU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "w7IkwGdrguwDrar5+w0Z3va5wXyZ4VXJkDMISyRjPGo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YmJUoILTRJPhyIyWyXJTsQ6KSZHHbEpwPVup6Ldm/Ko=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FvMjcwVZJmfh6FP/yBg2wgskK+KHD8YVUY6WtrE8xbg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "h4HCtD4HyYz0nci49IVAa10Z4NJD/FHnRMV4sRX6qro=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "nC7BpXCmym+a0Is2kReM9cYN2M1Eh5rVo8fjms14Oiw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1qtVWaeVo649ZZZtN8gXbwLgMWGLhz8beODbvru0I7Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Ej+mC0QFyMNIiSjR939S+iGBm7dm+1xObu5IcF/OpbU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UQ8LbUG3cMegbr9yKfKanAPQE1EfPkFciVDrNqZ5GHY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4iI3mXIDjnX+ralk1HhJY43mZx2uTJM7hsv9MQzTX7E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0WQCcs3rvsasgohERHHCaBM4Iy6yomS4qJ5To3/yYiw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qDCTVPoue1/DOAGNAlUstdA9Sid8MgEY4e5EzHcVHRk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9F9Mus0UnlzHb8E8ImxgXtz6SU98YXD0JqswOKw/Bzs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pctHpHKVBBcsahQ6TNh6/1V1ZrqOtKSAPtATV6BJqh0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vfR3C/4cPkVdxtNaqtF/v635ONbhTf5WbwJM6s4EXNE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ejP43xUBIex6szDcqExAFpx1IE/Ksi5ywJ84GKDFRrs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jbP4AWYd3S2f3ejmMG7dS5IbrFol48UUoT+ve3JLN6U=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CiDifI7958sUjNqJUBQULeyF7x0Up3loPWvYKw9uAuw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "e2dQFsiHqd2BFHNhlSxocjd+cPs4wkcUW/CnCz4KNuM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PJFckVmzBipqaEqsuP2mkjhJE4qhw36NhfQ9DcOHyEU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "S3MeuJhET/B8VcfZYDR9fvX0nscDj416jdDekhmK11s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CGVHZRXpuNtQviDB2Kj03Q8uvs4w3RwTgV847R7GwPw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yUGgmgyLrxbEpDVy89XN3c2cmFpZXWWmuJ/35zVZ+Jw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "inb6Q97mL1a9onfNTT8v9wsoi/fz7KXKq3p8j90AU9c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CCyYx/4npq9xGO1lsCo8ZJhFO9/tN7DB+/DTE778rYg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "LNnYw4fwbiAZu0kBdAHPEm/OFnreS+oArdB5O/l/I98=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "P006SxmUS/RjiQJVYPdMFnNo3827GIEmSzagggkg05Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "oyvwY+WsnYV6UHuPki1o0ILJ2jN4uyXf9yaUNtZJyBA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "36Lk3RHWh1wmtCWC/Yj6jNIo17U5y6SofAgQjzjVxD8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vOOo8FqeHnuO9mqOYjIb4vgwIwVyXZ5Y+bY5d9tGFUM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "bJiDJjwQRNxqxlGjRm5lLziFhcfTDCnQ/qU1V85qcRg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2Qgrm1n0wUELAQnpkEiIHB856yv76q8jLbpiucetcm0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "5ciPOYxTK0WDwwYyfs7yiVymwtYQXDELLxmM4JLl4/o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "31dC2WUSIOKQc4jwT6PikfeYTwi80mTlh7P31T5KNQU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YluTV2Mu53EGCKLcWfHZb0BM/IPW2xJdG3vYlDMEsM4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dh/8lGo2Ek6KukSwutH6Q35iy8TgV0FN0SJqe0ZVHN8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EVw6HpIs3BKen2qY2gz4y5dw1JpXilfh07msZfQqJpc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FYolLla9L8EZMROEdWetozroU40Dnmwwx2jIMrr7c1A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "8M6k4QIutSIj6CM41vvkQtuFsaGrjoR9SZJVSLbfGKQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9LM0VoddDNHway442MqY+Z7vohB2UHau/cddshhzf40=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "66i8Ytco4Yq/FMl6pIRZazz3CZlu8fO2OI6Pne0pvHU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2a/HgX+MjZxjXtSvHgF1yEpHMJBkl8Caee8XrJtn0WM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "frhBM662c4ZVG7mWP8K/HhRjd01lydW/cPcHnDjifqc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6k1T7Q1t668PBqv6fwpVnT1HWh7Am5LtbKvwPJKcpGU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UlJ5Edfusp8S/Pyhw6KTglIejmbr1HO0zUeHn/qFETA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jsxsB+1ECB3assUdoC333do9tYH+LglHmVSJHy4N8Hg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2nzIQxGYF7j3bGsIesECEOqhObKs/9ywknPHeJ3yges=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xJYKtuWrX90JrJVoYtnwP7Ce59XQGFYoalxpNfBXEH0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "NLI5lriBTleGCELcHBtNnmnvwSRkHHaLOX4cKboMgTw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hUOQV0RmE5aJdJww1AR9rirJG4zOYPo+6cCkgn/BGvQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "h4G2Of76AgxcUziBwCyH+ayMOpdBWzg4yFrTfehSC2c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VuamM75RzGfQpj2/Y1jSVuQLrhy6OAwlZxjuQLB/9Ss=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kn9+hLq7hvw02xr9vrplOCDXKBTuFhfbX7d5v/l85Pg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fAiGqKyLZpGngBYFbtYUYt8LUrJ49vYafiboifTDjxs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BxRILymgfVJCczqjUIWXcfrfSgrrYkxTM5VTg0HkZLY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CrFY/PzfPU2zsFkGLu/dI6mEeizZzCR+uYgjZBAHro0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "AEbrIuwvXLTtYgMjOqnGQ8y8axUn5Ukrn7UZRSyfQVw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ouWeVH3PEFg+dKWlXc6BmqirJOaVWjJbMzZbCsce4dA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+hd6xFB+EG+kVP7WH4uMd1CLaWMnt5xJRaY/Guuga9Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zmpGalfAOL3gmcUMJYcLYIRT/2VDO/1Dw4KdYZoNcng=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2PbHAoM/46J2UIZ/vyksKzmVVfxA7YUyIxWeL/N/vBk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7fD9x+zk5MVFesb59Klqiwwmve7P5ON/5COURXj5smE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tlrNQ4jaq051iaWonuv1sSrYhKkL1LtNZuHsvATha3s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fBodm28iClNpvlRyVq0dOdXQ08S7/N3aDwid+PdWvRo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "O+/nnRqT3Zv7yMMGug8GhKHaWy6u7BfRGtZoj0sdN1c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "5AZZ/RTMY4Photnm/cpXZr/HnFRi3eljacMsipkJLHA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "oFVyo/kgoMxBIk2VE52ySSimeyU+Gr0EfCwapXnTpKA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Z8v59DfcnviA0mzvnUk+URVO0UuqAWvtarEgJva/n1c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "P64GOntZ+zBJEHkigoh9FSxSO+rJTqR20z5aiGQ9an4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xMbSuDPfWuO/Dm7wuVl06GnzG9uzTlJJX9vFy7boGlY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kXPB19mRClxdH2UsHwlttS6lLU2uHvzuZgZz7kC45jU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "NDVjVYXAw4k0w4tFzvs7QDq39aaU3HQor4I2XMKKnCk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uKw/+ErVfpTO1dGUfd3T/eWfZW3nUxXCdBGdjvHtZ88=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "av0uxEzWkizYWm0QUM/MN1hLibnxPvCWJKwjOV4yVQY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ERwUC47dvgOBzIsEESMIioLYbFOxOe8PtJTnmDkKuHM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2gseKlG5Le12fS/vj4eaED4lturF16kAgJ1TpW3HxEE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7Cvg0Y3j/5i2F1TeXxlMmU7xwif5dCmwkZAOrVC5K2Y=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Aggregate.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Aggregate.yml deleted file mode 100644 index c2c37c1691..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Aggregate.yml +++ /dev/null @@ -1,330 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDecimal': '0.0'}, 'max': {'$numberDecimal': '200.0'}, 'precision': {'$numberInt': '2'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range DecimalPrecision. Aggregate." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } - - name: aggregate - arguments: - pipeline: [{ $match: { "encryptedDecimalPrecision": { $gt: {$numberDecimal: "0" }} } }] - result: [*doc1] - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - aggregate: *collection_name - pipeline: [ - { - "$match": { - "encryptedDecimalPrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - } - } - ] - cursor: {} - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: aggregate - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": { - "$numberInt": "0" - }, - "encryptedDecimalPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", - "subType": "00" - } - } - ] - } - - - { - "_id": { - "$numberInt": "1" - }, - "encryptedDecimalPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Correctness.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Correctness.yml deleted file mode 100644 index 6c54990c7c..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Correctness.yml +++ /dev/null @@ -1,425 +0,0 @@ -# Test correctness results. -# Does not include command monitoring expectations or outcome assertions to make tests more readable. - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDecimal': '0.0'}, 'max': {'$numberDecimal': '200.0'}, 'precision': {'$numberInt': '2'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "Find with $gt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0.0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1.0" } } - - name: find - arguments: - filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0.0" } }} - result: [*doc1] - - - description: "Find with $gte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalPrecision: { $gte: { $numberDecimal: "0.0" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $gt with no results" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "1.0" } }} - result: [] - - - description: "Find with $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "1.0" } }} - result: [*doc0] - - - description: "Find with $lte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalPrecision: { $lte: { $numberDecimal: "1.0" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $lt below min" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "0.0" } }} - result: - errorContains: must be greater than the range minimum - - - description: "Find with $gt above max" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "200.0" } }} - result: - errorContains: must be less than the range max - - - description: "Find with $gt and $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} - result: [*doc1] - - - description: "Find with equality" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalPrecision: { $numberDecimal: "0.0" } } - result: [*doc0] - - name: find - arguments: - filter: { encryptedDecimalPrecision: { $numberDecimal: "1.0" } } - result: [*doc1] - - - description: "Find with full range" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalPrecision: { $gte: {$numberDecimal: "0.0"}, $lte: {$numberDecimal: "200.0"} } } - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $in" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDecimalPrecision: { $in: [ {$numberDecimal: "0.0"} ] } } - result: [*doc0] - - - description: "Insert out of range" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "-1" }} - result: - errorContains: value must be greater than or equal to the minimum value - - - description: "Insert min and max" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: *doc0 - - name: insertOne - arguments: - document: &doc200 { _id: 200, encryptedDecimalPrecision: { $numberDecimal: "200.0" }} - - name: find - arguments: - filter: {} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc200] - - - description: "Aggregate with $gte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalPrecision: { $gte: { $numberDecimal: "0.0" } }} } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $gt with no results" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "1.0" } }} } - result: [] - - - description: "Aggregate with $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "1.0" } }} } - result: [*doc0] - - - description: "Aggregate with $lte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalPrecision: { $lte: { $numberDecimal: "1.0" } }} } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $lt below min" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "0.0" } }} } - result: - errorContains: must be greater than the range minimum - - - description: "Aggregate with $gt above max" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "200.0" } }} } - result: - errorContains: must be less than the range max - - - description: "Aggregate with $gt and $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} } - result: [*doc1] - - - description: "Aggregate with equality" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalPrecision: { $numberDecimal: "0.0" } } } - result: [*doc0] - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalPrecision: { $numberDecimal: "1.0" } } } - result: [*doc1] - - - description: "Aggregate with full range" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalPrecision: { $gte: {$numberDecimal: "0.0"}, $lte: {$numberDecimal: "200.0"} } } } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $in" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDecimalPrecision: { $in: [ {$numberDecimal: "0.0"} ] } } } - result: [*doc0] - - - description: "Wrong type: Insert Int" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: { _id: 0, encryptedDecimalPrecision: { $numberInt: "0" }} } - result: - # Expect an error from mongocryptd. - errorContains: "cannot encrypt element" - - - description: "Wrong type: Find Int" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: find - arguments: - filter: { encryptedDecimalPrecision: { $gte: { $numberInt: "0" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: - # expect an error from libmongocrypt. - errorContains: "field type is not supported" \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Delete.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Delete.yml deleted file mode 100644 index 14e115b5a1..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Delete.yml +++ /dev/null @@ -1,227 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDecimal': '0.0'}, 'max': {'$numberDecimal': '200.0'}, 'precision': {'$numberInt': '2'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range DecimalPrecision. Delete." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } - - name: deleteOne - arguments: - filter: { "encryptedDecimalPrecision": { $gt: {$numberDecimal: "0" }} } - result: - deletedCount: 1 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - delete: *collection_name - deletes: [ - { - "q": { - "encryptedDecimalPrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - }, - "limit": 1 - } - ] - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: delete - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": { - "$numberInt": "0" - }, - "encryptedDecimalPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.yml deleted file mode 100644 index ca8e383e07..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.yml +++ /dev/null @@ -1,328 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDecimal': '0.0'}, 'max': {'$numberDecimal': '200.0'}, 'precision': {'$numberInt': '2'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range DecimalPrecision. FindOneAndUpdate." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } - - name: findOneAndUpdate - arguments: - filter: { encryptedDecimalPrecision: { $gt: {$numberDecimal: "0"}} } - update: { "$set": { "encryptedDecimalPrecision": {$numberDecimal: "2"}}} - returnDocument: Before - result: *doc1 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - findAndModify: *collection_name - query: { - "encryptedDecimalPrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - } - update: { "$set": {"encryptedDecimalPrecision": { $$type: "binData" }} } - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: findAndModify - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": { - "$numberInt": "0" - }, - "encryptedDecimalPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", - "subType": "00" - } - } - ] - } - - - { - "_id": { - "$numberInt": "1" - }, - "encryptedDecimalPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", - "subType": "00" - } - } - ] - } diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-InsertFind.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-InsertFind.yml deleted file mode 100644 index 0ce295ef69..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-InsertFind.yml +++ /dev/null @@ -1,320 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDecimal': '0.0'}, 'max': {'$numberDecimal': '200.0'}, 'precision': {'$numberInt': '2'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range DecimalPrecision. Insert and Find." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } - - name: find - arguments: - filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0" } } } - result: [*doc1] - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - find: *collection_name - filter: - "encryptedDecimalPrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: find - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDecimalPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedDecimalPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Update.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Update.yml deleted file mode 100644 index c0b52904a9..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Update.yml +++ /dev/null @@ -1,337 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDecimal': '0.0'}, 'max': {'$numberDecimal': '200.0'}, 'precision': {'$numberInt': '2'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range DecimalPrecision. Update." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } - - name: updateOne - arguments: - filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0" } } } - update: { "$set": { "encryptedDecimalPrecision": { $numberDecimal: "2" } }} - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command_name: update - command: - "update": "default" - "ordered": true - "updates": [ - { - "q": { - "encryptedDecimalPrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - }, - "u": { - "$set": { - "encryptedDecimalPrecision": { $$type: "binData" } - } - } - } - ] - encryptionInformation: - type: 1 - schema: - "default.default": - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - "$db": "default" - - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDecimalPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedDecimalPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Aggregate.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Aggregate.yml deleted file mode 100644 index 72e6ae7c34..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Aggregate.yml +++ /dev/null @@ -1,914 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoubleNoPrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Double. Aggregate." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } - - name: aggregate - arguments: - pipeline: [{ $match: { "encryptedDoubleNoPrecision": { $gt: {$numberDouble: "0" }} } }] - result: [*doc1] - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - aggregate: *collection_name - pipeline: [ - { - "$match": { - "encryptedDoubleNoPrecision": { - "$gt": { - "$binary": { - "base64": "DYckAAADcGF5bG9hZABXJAAABGcAQyQAAAMwAH0AAAAFZAAgAAAAAHgYoMGjEE6fAlAhICv0+doHcVX8CmMVxyq7+jlyGrvmBXMAIAAAAAC/5MQZgTHuIr/O5Z3mXPvqrom5JTQ8IeSpQGhO9sB+8gVsACAAAAAAuPSXVmJUAUpTQg/A9Bu1hYczZF58KEhVofakygbsvJQAAzEAfQAAAAVkACAAAAAA2kiWNvEc4zunJ1jzvuClFC9hjZMYruKCqAaxq+oY8EAFcwAgAAAAACofIS72Cm6s866UCk+evTH3CvKBj/uZd72sAL608rzTBWwAIAAAAADuCQ/M2xLeALF0UFZtJb22QGOhHmJv6xoO+kZIHcDeiAADMgB9AAAABWQAIAAAAABkfoBGmU3hjYBvQbjNW19kfXneBQsQQPRfUL3UAwI2cAVzACAAAAAAUpK2BUOqX/DGdX5YJniEZMWkofxHqeAbXceEGJxhp8AFbAAgAAAAAKUaLzIldNIZv6RHE+FwbMjzcNHqPESwF/37mm43VPrsAAMzAH0AAAAFZAAgAAAAAFNprhQ3ZwIcYbuzLolAT5n/vc14P9kUUQComDu6eFyKBXMAIAAAAAAcx9z9pk32YbPV/sfPZl9ALIEVsqoLXgqWLVK/tP+heAVsACAAAAAA/qxvuvJbAHwwhfrPVpmCFzNvg2cU/NXaWgqgYUZpgXwAAzQAfQAAAAVkACAAAAAAODI+pB2pCuB+YmNEUAgtMfNdt3DmSkrJ96gRzLphgb8FcwAgAAAAAAT7dewFDxUDECQ3zVq75/cUN4IP+zsqhkP5+czUwlJIBWwAIAAAAACFGeOtd5zBXTJ4JYonkn/HXZfHipUlqGwIRUcH/VTatwADNQB9AAAABWQAIAAAAACNAk+yTZ4Ewk1EnotQK8O3h1gg9I7pr9q2+4po1iJVgAVzACAAAAAAUj/LesmtEsgqYVzMJ67umVA11hJTdDXwbxDoQ71vWyUFbAAgAAAAABlnhpgTQ0WjLb5u0b/vEydrCeFjVynKd7aqb+UnvVLeAAM2AH0AAAAFZAAgAAAAAD/FIrGYFDjyYmVb7oTMVwweWP7A6F9LnyIuNO4MjBnXBXMAIAAAAACIZgJCQRZu7NhuNMyOqCn1tf+DfU1qm10TPCfj5JYV3wVsACAAAAAA5hmY4ptuNxULGf87SUFXQWGAONsL9U29duh8xqsHtxoAAzcAfQAAAAVkACAAAAAAciRW40ORJLVwchOEpz87Svb+5toAFM6LxDWv928ECwQFcwAgAAAAAN0dipyESIkszfjRzdDi8kAGaa2Hf4wrPAtiWwboZLuxBWwAIAAAAAANr4o/+l1OIbbaX5lZ3fQ/WIeOcEXjNI1F0WbSgQrzaQADOAB9AAAABWQAIAAAAACZqAyCzYQupJ95mrBJX54yIz9VY7I0WrxpNYElCI4dTQVzACAAAAAA/eyJb6d1xfE+jJlVXMTD3HS/NEYENPVKAuj56Dr2dSEFbAAgAAAAANkSt154Or/JKb31VvbZFV46RPgUp8ff/hcPORL7PpFBAAM5AH0AAAAFZAAgAAAAAI5bm3YO0Xgf0VT+qjVTTfvckecM3Cwqj7DTKZXf8/NXBXMAIAAAAAD/m+h8fBhWaHm6Ykuz0WX1xL4Eme3ErLObyEVJf8NCywVsACAAAAAAfb1VZZCqs2ivYbRzX4p5CtaCkKW+g20Pr57FWXzEZi8AAzEwAH0AAAAFZAAgAAAAANqo4+p6qdtCzcB4BX1wQ6llU7eFBnuu4MtZwp4B6mDlBXMAIAAAAAAGiz+VaukMZ+6IH4jtn4KWWdKK4/W+O+gRioQDrfzpMgVsACAAAAAAG4YYkTp80EKo59mlHExDodRQFR7njhR5dmISwUJ6ukAAAzExAH0AAAAFZAAgAAAAAPrFXmHP2Y4YAm7b/aqsdn/DPoDkv7B8egWkfe23XsM1BXMAIAAAAAAGhwpKAr7skeqHm3oseSbO7qKNhmYsuUrECBxJ5k+D2AVsACAAAAAAAqPQi9luYAu3GrFCEsVjd9z2zIDcp6SPTR2w6KQEr+IAAzEyAH0AAAAFZAAgAAAAABzjYxwAjXxXc0Uxv18rH8I3my0Aguow0kTwKyxbrm+cBXMAIAAAAADVbqJVr6IdokuhXkEtXF0C2gINLiAjMVN20lE20Vmp2QVsACAAAAAAD7K1Fx4gFaaizkIUrf+EGXQeG7QX1jadhGc6Ji471H8AAzEzAH0AAAAFZAAgAAAAAFMm2feF2fFCm/UC6AfIyepX/xJDSmnnolQIBnHcPmb5BXMAIAAAAABLI11kFrQoaNVZFmq/38aRNImPOjdJh0Lo6irI8M/AaAVsACAAAAAAOWul0oVqJ9CejD2RqphhTC98DJeRQy5EwbNerU2+4l8AAzE0AH0AAAAFZAAgAAAAAJvXB3KyNiNtQko4SSzo/9b2qmM2zU9CQTTDfLSBWMgRBXMAIAAAAAAvjuVP7KsLRDeqVqRziTKpBrjVyqKiIbO9Gw8Wl2wFTAVsACAAAAAADlE+oc1ins+paNcaOZJhBlKlObDJ4VQORWjFYocM4LgAAzE1AH0AAAAFZAAgAAAAAPGdcxDiid8z8XYnfdDivNMYVPgBKdGOUw6UStU+48CdBXMAIAAAAAARj6g1Ap0eEfuCZ4X2TsEw+Djrhto3fA5nLwPaY0vCTgVsACAAAAAAoHqiwGOUkBu8SX5U1yHho+UIFdSN2MdQN5s6bQ0EsJYAAzE2AH0AAAAFZAAgAAAAAP5rGPrYGt3aKob5f/ldP0qrW7bmWvqnKY4QwdDWz400BXMAIAAAAADTQkW2ymaaf/bhteOOGmSrIR97bAnJx+yN3yMj1bTeewVsACAAAAAADyQnHGH2gF4w4L8axUsSTf6Ubk7L5/eoFOJk12MtZAoAAzE3AH0AAAAFZAAgAAAAAAlz6wJze5UkIxKpJOZFGCOf3v2KByWyI6NB6JM9wNcBBXMAIAAAAABUC7P/neUIHHoZtq0jFVBHY75tSFYr1Y5S16YN5XxC1QVsACAAAAAAgvxRbXDisNnLY3pfsjDdnFLtkvYUC4lhA68eBXc7KAwAAzE4AH0AAAAFZAAgAAAAAFJ8AtHcjia/9Y5pLEc3qVgH5xKiXw12G9Kn2A1EY8McBXMAIAAAAAAxe7Bdw7eUSBk/oAawa7uicTEDgXLymRNhBy1LAxhDvwVsACAAAAAAxKPaIBKVx3jTA+R/el7P7AZ7efrmTGjJs3Hj/YdMddwAAzE5AH0AAAAFZAAgAAAAAO8uwQUaKFb6vqR3Sv3Wn4QAonC2exOC9lGG1juqP5DtBXMAIAAAAABZf1KyJgQg8/Rf5c02DgDK2aQu0rNCOvaL60ohDHyY+gVsACAAAAAAqyEjfKC8lYoIfoXYHUqHZPoaA6EK5BAZy5dxXZmay4kAAzIwAH0AAAAFZAAgAAAAAE8YtqyRsGCeiR6hhiyisR/hccmK4nZqIMzO4lUBmEFzBXMAIAAAAAC1UYOSKqAeG1UJiKjWFVskRhuFKpj9Ezy+lICZvFlN5AVsACAAAAAA6Ct9nNMKyRazn1OKnRKagm746CGu+jyhbL1qJnZxGi0AAzIxAH0AAAAFZAAgAAAAAPhCrMausDx1QUIEqp9rUdRKyM6a9AAx7jQ3ILIu8wNIBXMAIAAAAACmH8lotGCiF2q9VQxhsS+7LAZv79VUAsOUALaGxE/EpAVsACAAAAAAnc1xCKfdvbUEc8F7XZqlNn1C+hZTtC0I9I3LL06iaNkAAzIyAH0AAAAFZAAgAAAAAOBi/GAYFcstMSJPgp3VkMiuuUUCrZytvqYaU8dwm8v2BXMAIAAAAACEZSZVyD3pKzGlbdwlYmWQhHHTV5SnNLknl2Gw8IaUTQVsACAAAAAAfsLZsEDcWSuNsIo/TD1ReyQW75HPMgmuKZuWFOLKRLoAAzIzAH0AAAAFZAAgAAAAAIQuup+YGfH3mflzWopN8J1X8o8a0d9CSGIvrA5HOzraBXMAIAAAAADYvNLURXsC2ITMqK14LABQBI+hZZ5wNf24JMcKLW+84AVsACAAAAAACzfjbTBH7IwDU91OqLAz94RFkoqBOkzKAqQb55gT4/MAAzI0AH0AAAAFZAAgAAAAAKsh0ADyOnVocFrOrf6MpTrNvAj8iaiE923DPryu124gBXMAIAAAAADg24a8NVE1GyScc6tmnTbmu5ulzO+896fE92lN08MeswVsACAAAAAAaPxcOIxnU7But88/yadOuDJDMcCywwrRitaxMODT4msAAzI1AH0AAAAFZAAgAAAAAKkVC2Y6HtRmv72tDnPUSjJBvse7SxLqnr09/Uuj9sVVBXMAIAAAAABYNFUkH7ylPMN+Bc3HWX1e0flGYNbtJNCY9SltJCW/UAVsACAAAAAAZYK/f9H4OeihmpiFMH7Wm7uLvs2s92zNA8wyrNZTsuMAAzI2AH0AAAAFZAAgAAAAADDggcwcb/Yn1Kk39sOHsv7BO/MfP3m/AJzjGH506Wf9BXMAIAAAAAAYZIsdjICS0+BDyRUPnrSAZfPrwtuMaEDEn0/ijLNQmAVsACAAAAAAGPnYVvo2ulO9z4LGd/69NAklfIcZqZvFX2KK0s+FcTUAAzI3AH0AAAAFZAAgAAAAAEWY7dEUOJBgjOoWVht1wLehsWAzB3rSOBtLgTuM2HC8BXMAIAAAAAAAoswiHRROurjwUW8u8D5EUT+67yvrgpB/j6PzBDAfVwVsACAAAAAA6NhRTYFL/Sz4tao7vpPjLNgAJ0FX6P/IyMW65qT6YsMAAzI4AH0AAAAFZAAgAAAAAPZaapeAUUFPA7JTCMOWHJa9lnPFh0/gXfAPjA1ezm4ZBXMAIAAAAACmJvLY2nivw7/b3DOKH/X7bBXjJwoowqb1GtEFO3OYgAVsACAAAAAA/JcUoyKacCB1NfmH8vYqC1f7rd13KShrQqV2r9QBP44AAzI5AH0AAAAFZAAgAAAAAK00u6jadxCZAiA+fTsPVDsnW5p5LCr4+kZZZOTDuZlfBXMAIAAAAAAote4zTEYMDgaaQbAdN8Dzv93ljPLdGjJzvnRn3KXgtQVsACAAAAAAxXd9Mh6R3mnJy8m7UfqMKi6oD5DlZpkaOz6bEjMOdiwAAzMwAH0AAAAFZAAgAAAAAFbgabdyymiEVYYwtJSWa7lfl/oYuj/SukzJeDOR6wPVBXMAIAAAAADAFGFjS1vPbN6mQEhkDYTD6V2V23Ys9gUEUMGNvMPkaAVsACAAAAAAL/D5Sze/ZoEanZLK0IeEkhgVkxEjMWVCfmJaD3a8uNIAAzMxAH0AAAAFZAAgAAAAABNMR6UBv2E627CqLtQ/eDYx7OEwQ7JrR4mSHFa1N8tLBXMAIAAAAAAxH4gucI4UmNVB7625C6hFSVCuIpJO3lusJlPuL8H5EQVsACAAAAAAVLHNg0OUVqZ7WGOP53BkTap9FOw9dr1P4J8HxqFqU04AAzMyAH0AAAAFZAAgAAAAAG8cd6WBneNunlqrQ2EmNf35W7OGObGq9WL4ePX+LUDmBXMAIAAAAAAjJ2+sX87NSis9hBsgb1QprVRnO7Bf+GObCGoUqyPE4wVsACAAAAAAs9c9SM49/pWmyUQKslpt3RTMBNSRppfNO0JBvUqHPg0AAzMzAH0AAAAFZAAgAAAAAFWOUGkUpy8yf6gB3dio/aOfRKh7XuhvsUj48iESFJrGBXMAIAAAAAAY7sCDMcrUXvNuL6dO0m11WyijzXZvPIcOKob6IpC4PQVsACAAAAAAJOP+EHz6awDb1qK2bZQ3kTV7wsj5Daj/IGAWh4g7omAAAzM0AH0AAAAFZAAgAAAAAGUrIdKxOihwNmo6B+aG+Ag1qa0+iqdksHOjQj+Oy9bZBXMAIAAAAABwa5dbI2KmzBDNBTQBEkjZv4sPaeRkRNejcjdVymRFKQVsACAAAAAA4ml/nm0gJNTcJ4vuD+T2Qfq2fQZlibJp/j6MOGDrbHMAAzM1AH0AAAAFZAAgAAAAAOx89xV/hRk64/CkM9N2EMK6aldII0c8smdcsZ46NbP8BXMAIAAAAADBF6tfQ+7q9kTuLyuyrSnDgmrdmrXkdhl980i1KHuGHgVsACAAAAAACUqiFqHZdGbwAA+hN0YUE5zFg+H+dabIB4dj5/75W/YAAzM2AH0AAAAFZAAgAAAAAMkN0L1oQWXhjwn9rAdudcYeN8/5VdCKU8cmDt7BokjsBXMAIAAAAAAT62pGXoRwExe9uvgYOI0hg5tOxilrWfoEmT0SMglWJwVsACAAAAAAlVz4dhiprSbUero6JFfxzSJGclg63oAkAmgbSwbcYxIAAzM3AH0AAAAFZAAgAAAAANxfa4xCoaaB7k1C1RoH1LBhsCbN2yEq15BT9b+iqEC4BXMAIAAAAACAX9LV8Pemfw7NF0iB1/85NzM1Ef+1mUfyehacUVgobQVsACAAAAAAVq4xpbymLk0trPC/a2MvB39I7hRiX8EJsVSI5E5hSBkAAzM4AH0AAAAFZAAgAAAAAOYIYoWkX7dGuyKfi3XssUlc7u/gWzqrR9KMkikKVdmSBXMAIAAAAABVF2OYjRTGi9Tw8XCAwZWLpX35Yl271TlNWp6N/nROhAVsACAAAAAA0nWwYzXQ1+EkDvnGq+SMlq20z+j32Su+i/A95SggPb4AAzM5AH0AAAAFZAAgAAAAAIy0+bXZi10QC+q7oSOLXK5Fee7VEk/qHSXukfeVIfgzBXMAIAAAAAAQ3IIV/JQCHW95AEbH5zGIHtJqyuPjWPMIZ+VmQHlxEwVsACAAAAAAp0jYsyohKv9Pm+4k+DplEGbl9WLZpAJzitrcDj4CNsMAAzQwAH0AAAAFZAAgAAAAAL5SOJQ3LOhgdXJ5v086NNeAl1qonQnchObdpZJ1kHeEBXMAIAAAAAA+tEqTXODtik+ydJZSnUqXF9f18bPeze9eWtR7ExZJgQVsACAAAAAAbrkZCVgB9Qsp4IAbdf+bD4fT6Boqk5UtuA/zhNrh1y0AAzQxAH0AAAAFZAAgAAAAAKl8zcHJRDjSjJeV/WvMxulW1zrTFtaeBy/aKKhadc6UBXMAIAAAAADBdWQl5SBIvtZZLIHszePwkO14W1mQ0izUk2Ov21cPNAVsACAAAAAAHErCYycpqiIcCZHdmPL1hi+ovLQk4TAvENpfLdTRamQAAzQyAH0AAAAFZAAgAAAAAFvotcNaoKnVt5CBCOPwjexFO0WGWuaIGL6H/6KSau+6BXMAIAAAAAD2y2mBN5xPu5PJoY2zcr0GnQDtHRBogA5+xzIxccE9fwVsACAAAAAAdS34xzJesnUfxLCcc1U7XzUqLy8MAzV/tcjbqaD3lkMAAzQzAH0AAAAFZAAgAAAAAPezU0/vNT4Q4YKbTbaeHqcwNLT+IjW/Y9QFpIooihjPBXMAIAAAAACj2x4O4rHter8ZnTws5LAP9jJ/6kk9C/V3vL50LoFZHAVsACAAAAAAQdBDF3747uCVP5lB/zr8VmzxJfTSZHBKeIgm5FyONXwAAzQ0AH0AAAAFZAAgAAAAAMqpayM2XotEFmm0gwQd9rIzApy0X+7HfOhNk6VU7F5lBXMAIAAAAACJR9+q5T9qFHXFNgGbZnPubG8rkO6cwWhzITQTmd6VgwVsACAAAAAAOZLQ6o7e4mVfDzbpQioa4d3RoTvqwgnbmc5Qh2wsZuoAAzQ1AH0AAAAFZAAgAAAAANCeyW+3oebaQk+aqxNVhAcT/BZ5nhsTVdKS3tMrLSvWBXMAIAAAAADxRFMDhkyuEc++WnndMfoUMLNL7T7rWoeblcrpSI6soQVsACAAAAAAdBuBMJ1lxt0DRq9pOZldQqchLs3B/W02txcMLD490FEAAzQ2AH0AAAAFZAAgAAAAAIbo5YBTxXM7HQhl7UP9NNgpPGFkBx871r1B65G47+K8BXMAIAAAAAC21dJSxnEhnxO5gzN5/34BL4von45e1meW92qowzb8fQVsACAAAAAAm3Hk2cvBN0ANaR5jzeZE5TsdxDvJCTOT1I01X7cNVaYAAzQ3AH0AAAAFZAAgAAAAABm/6pF96j26Jm7z5KkY1y33zcAEXLx2n0DwC03bs/ixBXMAIAAAAAD01OMvTZI/mqMgxIhA5nLs068mW+GKl3OW3ilf2D8+LgVsACAAAAAAaLvJDrqBESTNZSdcXsd+8GXPl8ZkUsGpeYuyYVv/kygAAzQ4AH0AAAAFZAAgAAAAAJ/D3+17gaQdkBqkL2wMwccdmCaVOtxzIkM8VyI4xI5zBXMAIAAAAAAggLVmkc5u+YzBR+oNE+XgLVp64fC6MzUb/Ilu/Jsw0AVsACAAAAAACz3HVKdWkx82/kGbVpcbAeZtsj2R5Zr0dEPfle4IErkAAzQ5AH0AAAAFZAAgAAAAAJMRyUW50oaTzspS6A3TUoXyC3gNYQoShUGPakMmeVZrBXMAIAAAAACona2Pqwt4U2PmFrtmu37jB9kQ/12okyAVtYa8TQkDiQVsACAAAAAAltJJKjCMyBTJ+4PkdDCPJdeX695P8P5h7WOZ+kmExMAAAzUwAH0AAAAFZAAgAAAAAByuYl8dBvfaZ0LO/81JW4hYypeNmvLMaxsIdvqMPrWoBXMAIAAAAABNddwobOUJzm9HOUD8BMZJqkNCUCqstHZkC76FIdNg9AVsACAAAAAAQQOkIQtkyNavqCnhQbNg3HfqrJdsAGaoxSJePJl1qXsAAzUxAH0AAAAFZAAgAAAAAHEzLtfmF/sBcYPPdj8867VmmQyU1xK9I/3Y0478azvABXMAIAAAAAAcmyFajZPnBTbO+oLInNwlApBocUekKkxz2hYFeSlQ+gVsACAAAAAAZ6IkrOVRcC8vSA6Vb4fPWZJrYexXhEabIuYIeXNsCSgAAzUyAH0AAAAFZAAgAAAAAJam7JYsZe2cN20ZYm2W3v1pisNt5PLiniMzymBLWyMtBXMAIAAAAABxCsKVMZMTn3n+R2L7pVz5nW804r8HcK0mCBw3jUXKXAVsACAAAAAA7j3JGnNtR64P4dJLeUoScFRGfa8ekjh3dvhw46sRFk0AAzUzAH0AAAAFZAAgAAAAAMXrXx0saZ+5gORmwM2FLuZG6iuO2YS+1IGPoAtDKoKBBXMAIAAAAADIQsxCr8CfFKaBcx8kIeSywnGh7JHjKRJ9vJd9x79y7wVsACAAAAAAcvBV+SykDYhmRFyVYwFYB9oBKBSHr55Jdz2cXeowsUQAAzU0AH0AAAAFZAAgAAAAACbzcUD3INSnCRspOKF7ubne74OK9L0FTZvi9Ay0JVDYBXMAIAAAAADPebVQH8Btk9rhBIoUOdSAdpPvz7qIY4UC2i6IGisSAQVsACAAAAAAiBunJi0mPnnXdnldiq+If8dcb/n6apHnaIFt+oyYO1kAAzU1AH0AAAAFZAAgAAAAACUc2CtD1MK/UTxtv+8iA9FoHEyTwdl43HKeSwDw2Lp5BXMAIAAAAACCIduIdw65bQMzRYRfjBJj62bc69T4QqH4QoWanwlvowVsACAAAAAAM0TV7S+aPVVzJOQ+cpSNKHTwyQ0mWa8tcHzfk3nR+9IAAzU2AH0AAAAFZAAgAAAAAHSaHWs/dnmI9sc7nB50VB2Bzs0kHapMHCQdyVEYY30TBXMAIAAAAACkV22lhEjWv/9/DubfHBAcwJggKI5mIbSK5L2nyqloqQVsACAAAAAAS19m7DccQxgryOsBJ3GsCs37yfQqNi1G+S6fCXpEhn4AAzU3AH0AAAAFZAAgAAAAAAL8jhNBG0KXXZhmZ0bPXtfgapJCB/AI+BEHB0eZ3C75BXMAIAAAAADHx/fPa639EBmGV5quLi8IQT600ifiKSOhTDOK19DnzwVsACAAAAAAlyLTDVkHxbayklD6Qymh3odIK1JHaOtps4f4HR+PcDgAAzU4AH0AAAAFZAAgAAAAAAxgeclNl09H7HvzD1oLwb2YpFca5eaX90uStYXHilqKBXMAIAAAAACMU5pSxzIzWlQxHyW170Xs9EhD1hURASQk+qkx7K5Y6AVsACAAAAAAJbMMwJfNftA7Xom8Bw/ghuZmSa3x12vTZxBUbV8m888AAzU5AH0AAAAFZAAgAAAAABmO7QD9vxWMmFjIHz13lyOeV6vHT6mYCsWxF7hb/yOjBXMAIAAAAACT9lmgkiqzuWG24afuzYiCeK9gmJqacmxAruIukd0xEAVsACAAAAAAZa0/FI/GkZR7CtX18Xg9Tn9zfxkD0UoaSt+pIO5t1t4AAzYwAH0AAAAFZAAgAAAAAB89SjLtDJkqEghRGyj6aQ/2qvWLNuMROoXmzbYbCMKMBXMAIAAAAAC8sywgND+CjhVTF6HnRQeay8y9/HnVzDI42dEPah28LQVsACAAAAAAoxv7UKh0RqUAWcOsQvU123zO1qZn73Xfib0qncZCB34AAzYxAH0AAAAFZAAgAAAAABN2alGq9Aats1mwERNGwL/fIwZSvVCe9/8XMHTFlpUpBXMAIAAAAACuDPjJgvvbBYhbLpjMiWUCsVppiYrhvR+yMysNPN8cZAVsACAAAAAAKpADjc4bzIZMi9Q/+oe0EMRJHYQt6dlo1x/lRquagqkAAzYyAH0AAAAFZAAgAAAAAL8YB6VAqGBiWD4CBv16IBscg5J7VQCTZu87n6pj+86KBXMAIAAAAAAmxm8e68geeyAdUjSMWBHzUjneVB0pG9TBXIoE6467hAVsACAAAAAAV76JZAlYpgC/Zl8awx2ArCg1uuyy2XVTSkp0wUMi/7UAAzYzAH0AAAAFZAAgAAAAAL4yLkCTV5Dmxa5toBu4JT8ge/cITAaURIOuFuOtFUkeBXMAIAAAAAAXoFNQOMGkAj7qEJP0wQafmFSXgWGeorDVbwyOxWLIsgVsACAAAAAAc4Un6dtIFe+AQ+RSfNWs3q63RTHhmyc+5GKRRdpWRv8AAzY0AH0AAAAFZAAgAAAAAEU8DoUp46YtYjNFS9kNXwdYxQ9IW27vCTb+VcqqfnKNBXMAIAAAAADe7vBOgYReE8X78k5ARuUnv4GmzPZzg6SbConf4L2G3wVsACAAAAAA78YHWVkp6HbZ0zS4UL2z/2pj9vPDcMDt7zTv6NcRsVsAAzY1AH0AAAAFZAAgAAAAAPa4yKTtkUtySuWo1ZQsp2QXtPb5SYqzA5vYDnS1P6c0BXMAIAAAAADKnF58R1sXlHlsHIvCBR3YWW/qk54z9CTDhZydkD1cOQVsACAAAAAAHW3ERalTFWKMzjuXF3nFh0pSrQxM/ojnPbPhc4v5MaQAAzY2AH0AAAAFZAAgAAAAAN5WJnMBmfgpuQPyonmY5X6OdRvuHw4nhsnGRnFAQ95VBXMAIAAAAACwftzu7KVV1rmGKwXtJjs3cJ1gE3apr8+N0SAg1F2cHwVsACAAAAAATDW0reyaCjbJuVLJzbSLx1OBuBoQu+090kgW4RurVacAAzY3AH0AAAAFZAAgAAAAACHvDsaPhoSb6DeGnKQ1QOpGYAgK82qpnqwcmzSeWaJHBXMAIAAAAABRq3C5+dOfnkAHM5Mg5hPB3O4jhwQlBgQWLA7Ph5bhgwVsACAAAAAAqkC8zYASvkVrp0pqmDyFCkPaDmD/ePAJpMuNOCBhni8AAzY4AH0AAAAFZAAgAAAAAOBePJvccPMJmy515KB1AkXF5Pi8NOG4V8psWy0SPRP+BXMAIAAAAAB3dOJG9xIDtEKCRzeNnPS3bFZepMj8UKBobKpSoCPqpgVsACAAAAAAPG3IxQVOdZrr509ggm5FKizWWoZPuVtOgOIGZ3m+pdEAAzY5AH0AAAAFZAAgAAAAABUvRrDQKEXLMdhnzXRdhiL6AGNs2TojPky+YVLXs+JnBXMAIAAAAAD1kYicbEEcPzD4QtuSYQQWDPq8fuUWGddpWayKn3dT9QVsACAAAAAA9+Sf7PbyFcY45hP9oTfjQiOUS3vEIAT8C0vOHymwYSUAAzcwAH0AAAAFZAAgAAAAAOvSnpujeKNen4pqc2HR63C5s5oJ1Vf4CsbKoYQvkwl5BXMAIAAAAACw2+vAMdibzd2YVVNfk81yXkFZP0WLJ82JBxJmXnYE+QVsACAAAAAArQ/E1ACyhK4ZyLqH9mNkCU7WClqRQTGyW9tciSGG/EMAAzcxAH0AAAAFZAAgAAAAAAo0xfGG7tJ3GWhgPVhW5Zn239nTD3PadShCNRc9TwdNBXMAIAAAAADZh243oOhenu0s/P/5KZLBDh9ADqKHtSWcXpO9D2sIjgVsACAAAAAAlgTPaoQKz+saU8rwCT3UiNOdG6hdpjzFx9GBn08ZkBEAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - } - } - ] - cursor: {} - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: aggregate - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDoubleNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedDoubleNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2FIZh/9N+NeJEQwxYIX5ikQT85xJzulBNReXk8PnG/s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FWXI/yZ1M+2fIboeMCDMlp+I2NwPQDtoM/wWselOPYw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uk26nvN/LdRLaBphiBgIZzT0sSpoO1z0RdDWRm/xrSA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hiiYSH1KZovAULc7rlmEU74wCjzDR+mm6ZnsgvFQjMw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hRzvMvWPX0sJme+wck67lwbKDFaWOa+Eyef+JSdc1s4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PSx5D+zqC9c295dguX4+EobT4IEzfffdfjzC8DWpB5Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QzfXQCVTjPQv2h21v95HYPq8uCsVJ2tPnjv79gAaM9M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XcGDO/dlTcEMLqwcm55UmOqK+KpBmbzZO1LIzX7GPaQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Lf+o4E7YB5ynzUPC6KTyW0lj6Cg9oLIu1Sdd1ODHctA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wAuVn02LAVo5Y+TUocvkoenFYWzpu38k0NmGZOsAjS4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yJGDtveLbbo/0HtCtiTSsvVI/0agg/U1bFaQ0yhK12o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KsEy0zgYcmkM+O/fWF9z3aJGIk22XCk+Aw96HB6JU68=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "p+AnMI5ZxdJMSIEJmXXya+FeH5yubmOdViwUO89j0Rc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/jLix56jzeywBtNuGw55lCXyebQoSIhbful0hOKxKDY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fvDvSPomtJsl1S3+8/tzFCE8scHIdJY5hB9CdTEsoFo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "oV5hOJzPXxfTuRdKIlF4uYEoMDuqH+G7/3qgndDr0PM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "3ALwcvLj3VOfgD6OqXAO13h1ZkOv46R6+Oy6SUKh53I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gxaB9FJj0IM+InhvAjwWaex3UIZ9SAnDiUd5WHSY/l0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "66NPvDygJzKJqddfNuDuNOpvGajjFRtvhkwfUkiYmXw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1dWcQIocRAcO9XnXYqbhl83jc0RgjQpsrWd8dC27trg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "npos0Uf1DT3ztSCjPVY9EImlRnTHB1KLrvmVSqBQ/8E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "TEI9qBx/tK1l1H0v1scMG8Srmtwo5VxWHADPBSlWrXk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "3wUN2ypQKoj+5ASkeIK9ycxhahVxyTmGopigoUAlyYs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "o/oksSnUS+nIq6ozWTbB5bJh+NoaPj8deAA23uxiWCk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KExYPruhA31e8xuSwvfUfDcyY/H2Va6taUd0k4yFgLc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/x+dNfxdd/lkx8Z8VZVfoYl7LPoaZ/iKEzZXBrAtIJc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DE4cmjFLPqZlmRomO0qQiruUBtzoCe8ZdNRcfNH92pU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "M6EKNcLPw/iojAChgYUSieaBYWcbsjKtB94SaHOr8vk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+qP49lDPeyhaduTvXJgtJEqHNEYANVu9Bg3Bxz7Td9w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ruMrC2VIS+VKbJwCFb3bfkaLTju9nE+yPONV9s0M0Vo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EbjDlSB5JKnDKff4d8hOmaOwJ7B9Q6NQFisLj+DPC+0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "C/yYOTB94edyqAbiQNu8/H7FoG3yRRjHDkMykz4+Mv0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CBxqrejG+qQQq2YTd6iP/06kiu2CxxzBFaZK3Ofb1CM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2ZOQ/fpho+AbDENWBZaln7wRoepIRdhyT648dr8O5cU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EghIgEPz01+myPgj8oid+PgncvobvC7vjvG3THEEQ0M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "92CysZYNF8riwAMhdrIPKxfODw9p07cKQy/Snn8XmVY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VO0LeTBQmsEf7sCHzTnZwUPNTqRZ49R8V5E9XnZ/5N4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "exs8BQMJq7U6ZXYgIizT7XN+X/hOmmn4YEuzev9zgSI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qHpS4k1I+gPniNp4CA8TY8lLN36vBYmgbKMFpbYMEqg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+7lWKCKAWFw6gPZdHE6E8KIfI14/fSvtWUmllb5WLi0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YiH/US0q6679hWblFDDKNqUjCgggoU8sUCssTIF1QbU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YgwkKElEubNfvXL9hJxzqQUQtHiXN/OCGxNL1MUZZlM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hZFST4INZTTuhvJlGJeMwlUAK270UCOTCDeBAnN4a7g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "24I1Zw35AuGnK3CqJhbCwYb0IPuu5sCRrM5iyeITOLc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vgD12JB4Q1S/kGPSQ1KOgp386KnG1GbM/5+60oRGcGw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+wNE+OL+CB9d4AUJdVxd56jUJCAXmmk9fapuB2TAc4g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uhQh1B2Pe4RkNw/kPEcgaLenuikKoRf1iyfZhpXdodc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "eu8gjAUIp8ybO204AgeOq5v1neI1yljqy5v3I6lo1lM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7QG6oVbASBAjrnCPxzzUNnuFSFNlKhbuBafkF8pr7Is=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PUS1xb2oHSDTdYltutoSSxBiJ1NjxH3l2kA4P1CZLEs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XPMh/JDC/O93gJJCwwgJDb8ssWZvRvezNmKmyn3nIfk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jWz+KGwMk/GOvFAK2rOxF3OjxeZAWfmUQ1HGJ7icw4A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "o7XbW68pc6flYigf3LW4WAGUWxpeqxaQLkHUhUR9RZ8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "nqR+g60+5U0okbqJadSqGgnC+j1JcP8rwMcfzOs2ACI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Hz43qVK95tSfbYFtaE/8fE97XMk1RiO8XpWjwZHB80o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "noZUWlZ8M6KXU5rkifyo8/duw5IL7/fXbJvT7bNmW9k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WONVHCuPSanXDRQQ/3tmyJ0Vq+Lu/4hRaMUf0g0kSuw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UEaj6vQRoIghE8Movd8AGXhtwIOXlP4cBsECIUvE5Y8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "D3n2YcO8+PB4C8brDo7kxKjF9Y844rVkdRMLTgsQkrw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "C+YA0G9KjxZVaWwOMuh/dcnHnHAlYnbFrRl0IEpmsY0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rUnmbmQanxrbFPYYrwyQ53x66OSt27yAvF+s48ezKDc=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Correctness.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Correctness.yml deleted file mode 100644 index a1cbfbba3f..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Correctness.yml +++ /dev/null @@ -1,293 +0,0 @@ -# Test correctness results. -# Does not include command monitoring expectations or outcome assertions to make tests more readable. - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoubleNoPrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "Find with $gt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0.0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1.0" } } - - name: find - arguments: - filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0.0" } }} - result: [*doc1] - - - description: "Find with $gte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoubleNoPrecision: { $gte: { $numberDouble: "0.0" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $gt with no results" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "1.0" } }} - result: [] - - - description: "Find with $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoubleNoPrecision: { $lt: { $numberDouble: "1.0" } }} - result: [*doc0] - - - description: "Find with $lte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoubleNoPrecision: { $lte: { $numberDouble: "1.0" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $gt and $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} - result: [*doc1] - - - description: "Find with equality" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoubleNoPrecision: { $numberDouble: "0.0" } } - result: [*doc0] - - name: find - arguments: - filter: { encryptedDoubleNoPrecision: { $numberDouble: "1.0" } } - result: [*doc1] - - - description: "Find with $in" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoubleNoPrecision: { $in: [ {$numberDouble: "0.0"} ] } } - result: [*doc0] - - - description: "Aggregate with $gte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoubleNoPrecision: { $gte: { $numberDouble: "0.0" } }} } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $gt with no results" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "1.0" } }} } - result: [] - - - description: "Aggregate with $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoubleNoPrecision: { $lt: { $numberDouble: "1.0" } }} } - result: [*doc0] - - - description: "Aggregate with $lte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoubleNoPrecision: { $lte: { $numberDouble: "1.0" } }} } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $gt and $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} } - result: [*doc1] - - - description: "Aggregate with equality" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoubleNoPrecision: { $numberDouble: "0.0" } } } - result: [*doc0] - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoubleNoPrecision: { $numberDouble: "1.0" } } } - result: [*doc1] - - - description: "Aggregate with $in" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoubleNoPrecision: { $in: [ {$numberDouble: "0.0"} ] } } } - result: [*doc0] - - - description: "Wrong type: Insert Int" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: { _id: 0, encryptedDoubleNoPrecision: { $numberInt: "0" }} } - result: - # Expect an error from mongocryptd. - errorContains: "cannot encrypt element" - - - description: "Wrong type: Find Int" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: find - arguments: - filter: { encryptedDoubleNoPrecision: { $gte: { $numberInt: "0" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: - # expect an error from libmongocrypt. - errorContains: "field type is not supported" \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Delete.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Delete.yml deleted file mode 100644 index 1a83512902..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Delete.yml +++ /dev/null @@ -1,519 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoubleNoPrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Double. Delete." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } - - name: deleteOne - arguments: - filter: { "encryptedDoubleNoPrecision": { $gt: {$numberDouble: "0" }} } - result: - deletedCount: 1 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - delete: *collection_name - deletes: [ - { - "q": { - "encryptedDoubleNoPrecision": { - "$gt": { - "$binary": { - "base64": "DYckAAADcGF5bG9hZABXJAAABGcAQyQAAAMwAH0AAAAFZAAgAAAAAHgYoMGjEE6fAlAhICv0+doHcVX8CmMVxyq7+jlyGrvmBXMAIAAAAAC/5MQZgTHuIr/O5Z3mXPvqrom5JTQ8IeSpQGhO9sB+8gVsACAAAAAAuPSXVmJUAUpTQg/A9Bu1hYczZF58KEhVofakygbsvJQAAzEAfQAAAAVkACAAAAAA2kiWNvEc4zunJ1jzvuClFC9hjZMYruKCqAaxq+oY8EAFcwAgAAAAACofIS72Cm6s866UCk+evTH3CvKBj/uZd72sAL608rzTBWwAIAAAAADuCQ/M2xLeALF0UFZtJb22QGOhHmJv6xoO+kZIHcDeiAADMgB9AAAABWQAIAAAAABkfoBGmU3hjYBvQbjNW19kfXneBQsQQPRfUL3UAwI2cAVzACAAAAAAUpK2BUOqX/DGdX5YJniEZMWkofxHqeAbXceEGJxhp8AFbAAgAAAAAKUaLzIldNIZv6RHE+FwbMjzcNHqPESwF/37mm43VPrsAAMzAH0AAAAFZAAgAAAAAFNprhQ3ZwIcYbuzLolAT5n/vc14P9kUUQComDu6eFyKBXMAIAAAAAAcx9z9pk32YbPV/sfPZl9ALIEVsqoLXgqWLVK/tP+heAVsACAAAAAA/qxvuvJbAHwwhfrPVpmCFzNvg2cU/NXaWgqgYUZpgXwAAzQAfQAAAAVkACAAAAAAODI+pB2pCuB+YmNEUAgtMfNdt3DmSkrJ96gRzLphgb8FcwAgAAAAAAT7dewFDxUDECQ3zVq75/cUN4IP+zsqhkP5+czUwlJIBWwAIAAAAACFGeOtd5zBXTJ4JYonkn/HXZfHipUlqGwIRUcH/VTatwADNQB9AAAABWQAIAAAAACNAk+yTZ4Ewk1EnotQK8O3h1gg9I7pr9q2+4po1iJVgAVzACAAAAAAUj/LesmtEsgqYVzMJ67umVA11hJTdDXwbxDoQ71vWyUFbAAgAAAAABlnhpgTQ0WjLb5u0b/vEydrCeFjVynKd7aqb+UnvVLeAAM2AH0AAAAFZAAgAAAAAD/FIrGYFDjyYmVb7oTMVwweWP7A6F9LnyIuNO4MjBnXBXMAIAAAAACIZgJCQRZu7NhuNMyOqCn1tf+DfU1qm10TPCfj5JYV3wVsACAAAAAA5hmY4ptuNxULGf87SUFXQWGAONsL9U29duh8xqsHtxoAAzcAfQAAAAVkACAAAAAAciRW40ORJLVwchOEpz87Svb+5toAFM6LxDWv928ECwQFcwAgAAAAAN0dipyESIkszfjRzdDi8kAGaa2Hf4wrPAtiWwboZLuxBWwAIAAAAAANr4o/+l1OIbbaX5lZ3fQ/WIeOcEXjNI1F0WbSgQrzaQADOAB9AAAABWQAIAAAAACZqAyCzYQupJ95mrBJX54yIz9VY7I0WrxpNYElCI4dTQVzACAAAAAA/eyJb6d1xfE+jJlVXMTD3HS/NEYENPVKAuj56Dr2dSEFbAAgAAAAANkSt154Or/JKb31VvbZFV46RPgUp8ff/hcPORL7PpFBAAM5AH0AAAAFZAAgAAAAAI5bm3YO0Xgf0VT+qjVTTfvckecM3Cwqj7DTKZXf8/NXBXMAIAAAAAD/m+h8fBhWaHm6Ykuz0WX1xL4Eme3ErLObyEVJf8NCywVsACAAAAAAfb1VZZCqs2ivYbRzX4p5CtaCkKW+g20Pr57FWXzEZi8AAzEwAH0AAAAFZAAgAAAAANqo4+p6qdtCzcB4BX1wQ6llU7eFBnuu4MtZwp4B6mDlBXMAIAAAAAAGiz+VaukMZ+6IH4jtn4KWWdKK4/W+O+gRioQDrfzpMgVsACAAAAAAG4YYkTp80EKo59mlHExDodRQFR7njhR5dmISwUJ6ukAAAzExAH0AAAAFZAAgAAAAAPrFXmHP2Y4YAm7b/aqsdn/DPoDkv7B8egWkfe23XsM1BXMAIAAAAAAGhwpKAr7skeqHm3oseSbO7qKNhmYsuUrECBxJ5k+D2AVsACAAAAAAAqPQi9luYAu3GrFCEsVjd9z2zIDcp6SPTR2w6KQEr+IAAzEyAH0AAAAFZAAgAAAAABzjYxwAjXxXc0Uxv18rH8I3my0Aguow0kTwKyxbrm+cBXMAIAAAAADVbqJVr6IdokuhXkEtXF0C2gINLiAjMVN20lE20Vmp2QVsACAAAAAAD7K1Fx4gFaaizkIUrf+EGXQeG7QX1jadhGc6Ji471H8AAzEzAH0AAAAFZAAgAAAAAFMm2feF2fFCm/UC6AfIyepX/xJDSmnnolQIBnHcPmb5BXMAIAAAAABLI11kFrQoaNVZFmq/38aRNImPOjdJh0Lo6irI8M/AaAVsACAAAAAAOWul0oVqJ9CejD2RqphhTC98DJeRQy5EwbNerU2+4l8AAzE0AH0AAAAFZAAgAAAAAJvXB3KyNiNtQko4SSzo/9b2qmM2zU9CQTTDfLSBWMgRBXMAIAAAAAAvjuVP7KsLRDeqVqRziTKpBrjVyqKiIbO9Gw8Wl2wFTAVsACAAAAAADlE+oc1ins+paNcaOZJhBlKlObDJ4VQORWjFYocM4LgAAzE1AH0AAAAFZAAgAAAAAPGdcxDiid8z8XYnfdDivNMYVPgBKdGOUw6UStU+48CdBXMAIAAAAAARj6g1Ap0eEfuCZ4X2TsEw+Djrhto3fA5nLwPaY0vCTgVsACAAAAAAoHqiwGOUkBu8SX5U1yHho+UIFdSN2MdQN5s6bQ0EsJYAAzE2AH0AAAAFZAAgAAAAAP5rGPrYGt3aKob5f/ldP0qrW7bmWvqnKY4QwdDWz400BXMAIAAAAADTQkW2ymaaf/bhteOOGmSrIR97bAnJx+yN3yMj1bTeewVsACAAAAAADyQnHGH2gF4w4L8axUsSTf6Ubk7L5/eoFOJk12MtZAoAAzE3AH0AAAAFZAAgAAAAAAlz6wJze5UkIxKpJOZFGCOf3v2KByWyI6NB6JM9wNcBBXMAIAAAAABUC7P/neUIHHoZtq0jFVBHY75tSFYr1Y5S16YN5XxC1QVsACAAAAAAgvxRbXDisNnLY3pfsjDdnFLtkvYUC4lhA68eBXc7KAwAAzE4AH0AAAAFZAAgAAAAAFJ8AtHcjia/9Y5pLEc3qVgH5xKiXw12G9Kn2A1EY8McBXMAIAAAAAAxe7Bdw7eUSBk/oAawa7uicTEDgXLymRNhBy1LAxhDvwVsACAAAAAAxKPaIBKVx3jTA+R/el7P7AZ7efrmTGjJs3Hj/YdMddwAAzE5AH0AAAAFZAAgAAAAAO8uwQUaKFb6vqR3Sv3Wn4QAonC2exOC9lGG1juqP5DtBXMAIAAAAABZf1KyJgQg8/Rf5c02DgDK2aQu0rNCOvaL60ohDHyY+gVsACAAAAAAqyEjfKC8lYoIfoXYHUqHZPoaA6EK5BAZy5dxXZmay4kAAzIwAH0AAAAFZAAgAAAAAE8YtqyRsGCeiR6hhiyisR/hccmK4nZqIMzO4lUBmEFzBXMAIAAAAAC1UYOSKqAeG1UJiKjWFVskRhuFKpj9Ezy+lICZvFlN5AVsACAAAAAA6Ct9nNMKyRazn1OKnRKagm746CGu+jyhbL1qJnZxGi0AAzIxAH0AAAAFZAAgAAAAAPhCrMausDx1QUIEqp9rUdRKyM6a9AAx7jQ3ILIu8wNIBXMAIAAAAACmH8lotGCiF2q9VQxhsS+7LAZv79VUAsOUALaGxE/EpAVsACAAAAAAnc1xCKfdvbUEc8F7XZqlNn1C+hZTtC0I9I3LL06iaNkAAzIyAH0AAAAFZAAgAAAAAOBi/GAYFcstMSJPgp3VkMiuuUUCrZytvqYaU8dwm8v2BXMAIAAAAACEZSZVyD3pKzGlbdwlYmWQhHHTV5SnNLknl2Gw8IaUTQVsACAAAAAAfsLZsEDcWSuNsIo/TD1ReyQW75HPMgmuKZuWFOLKRLoAAzIzAH0AAAAFZAAgAAAAAIQuup+YGfH3mflzWopN8J1X8o8a0d9CSGIvrA5HOzraBXMAIAAAAADYvNLURXsC2ITMqK14LABQBI+hZZ5wNf24JMcKLW+84AVsACAAAAAACzfjbTBH7IwDU91OqLAz94RFkoqBOkzKAqQb55gT4/MAAzI0AH0AAAAFZAAgAAAAAKsh0ADyOnVocFrOrf6MpTrNvAj8iaiE923DPryu124gBXMAIAAAAADg24a8NVE1GyScc6tmnTbmu5ulzO+896fE92lN08MeswVsACAAAAAAaPxcOIxnU7But88/yadOuDJDMcCywwrRitaxMODT4msAAzI1AH0AAAAFZAAgAAAAAKkVC2Y6HtRmv72tDnPUSjJBvse7SxLqnr09/Uuj9sVVBXMAIAAAAABYNFUkH7ylPMN+Bc3HWX1e0flGYNbtJNCY9SltJCW/UAVsACAAAAAAZYK/f9H4OeihmpiFMH7Wm7uLvs2s92zNA8wyrNZTsuMAAzI2AH0AAAAFZAAgAAAAADDggcwcb/Yn1Kk39sOHsv7BO/MfP3m/AJzjGH506Wf9BXMAIAAAAAAYZIsdjICS0+BDyRUPnrSAZfPrwtuMaEDEn0/ijLNQmAVsACAAAAAAGPnYVvo2ulO9z4LGd/69NAklfIcZqZvFX2KK0s+FcTUAAzI3AH0AAAAFZAAgAAAAAEWY7dEUOJBgjOoWVht1wLehsWAzB3rSOBtLgTuM2HC8BXMAIAAAAAAAoswiHRROurjwUW8u8D5EUT+67yvrgpB/j6PzBDAfVwVsACAAAAAA6NhRTYFL/Sz4tao7vpPjLNgAJ0FX6P/IyMW65qT6YsMAAzI4AH0AAAAFZAAgAAAAAPZaapeAUUFPA7JTCMOWHJa9lnPFh0/gXfAPjA1ezm4ZBXMAIAAAAACmJvLY2nivw7/b3DOKH/X7bBXjJwoowqb1GtEFO3OYgAVsACAAAAAA/JcUoyKacCB1NfmH8vYqC1f7rd13KShrQqV2r9QBP44AAzI5AH0AAAAFZAAgAAAAAK00u6jadxCZAiA+fTsPVDsnW5p5LCr4+kZZZOTDuZlfBXMAIAAAAAAote4zTEYMDgaaQbAdN8Dzv93ljPLdGjJzvnRn3KXgtQVsACAAAAAAxXd9Mh6R3mnJy8m7UfqMKi6oD5DlZpkaOz6bEjMOdiwAAzMwAH0AAAAFZAAgAAAAAFbgabdyymiEVYYwtJSWa7lfl/oYuj/SukzJeDOR6wPVBXMAIAAAAADAFGFjS1vPbN6mQEhkDYTD6V2V23Ys9gUEUMGNvMPkaAVsACAAAAAAL/D5Sze/ZoEanZLK0IeEkhgVkxEjMWVCfmJaD3a8uNIAAzMxAH0AAAAFZAAgAAAAABNMR6UBv2E627CqLtQ/eDYx7OEwQ7JrR4mSHFa1N8tLBXMAIAAAAAAxH4gucI4UmNVB7625C6hFSVCuIpJO3lusJlPuL8H5EQVsACAAAAAAVLHNg0OUVqZ7WGOP53BkTap9FOw9dr1P4J8HxqFqU04AAzMyAH0AAAAFZAAgAAAAAG8cd6WBneNunlqrQ2EmNf35W7OGObGq9WL4ePX+LUDmBXMAIAAAAAAjJ2+sX87NSis9hBsgb1QprVRnO7Bf+GObCGoUqyPE4wVsACAAAAAAs9c9SM49/pWmyUQKslpt3RTMBNSRppfNO0JBvUqHPg0AAzMzAH0AAAAFZAAgAAAAAFWOUGkUpy8yf6gB3dio/aOfRKh7XuhvsUj48iESFJrGBXMAIAAAAAAY7sCDMcrUXvNuL6dO0m11WyijzXZvPIcOKob6IpC4PQVsACAAAAAAJOP+EHz6awDb1qK2bZQ3kTV7wsj5Daj/IGAWh4g7omAAAzM0AH0AAAAFZAAgAAAAAGUrIdKxOihwNmo6B+aG+Ag1qa0+iqdksHOjQj+Oy9bZBXMAIAAAAABwa5dbI2KmzBDNBTQBEkjZv4sPaeRkRNejcjdVymRFKQVsACAAAAAA4ml/nm0gJNTcJ4vuD+T2Qfq2fQZlibJp/j6MOGDrbHMAAzM1AH0AAAAFZAAgAAAAAOx89xV/hRk64/CkM9N2EMK6aldII0c8smdcsZ46NbP8BXMAIAAAAADBF6tfQ+7q9kTuLyuyrSnDgmrdmrXkdhl980i1KHuGHgVsACAAAAAACUqiFqHZdGbwAA+hN0YUE5zFg+H+dabIB4dj5/75W/YAAzM2AH0AAAAFZAAgAAAAAMkN0L1oQWXhjwn9rAdudcYeN8/5VdCKU8cmDt7BokjsBXMAIAAAAAAT62pGXoRwExe9uvgYOI0hg5tOxilrWfoEmT0SMglWJwVsACAAAAAAlVz4dhiprSbUero6JFfxzSJGclg63oAkAmgbSwbcYxIAAzM3AH0AAAAFZAAgAAAAANxfa4xCoaaB7k1C1RoH1LBhsCbN2yEq15BT9b+iqEC4BXMAIAAAAACAX9LV8Pemfw7NF0iB1/85NzM1Ef+1mUfyehacUVgobQVsACAAAAAAVq4xpbymLk0trPC/a2MvB39I7hRiX8EJsVSI5E5hSBkAAzM4AH0AAAAFZAAgAAAAAOYIYoWkX7dGuyKfi3XssUlc7u/gWzqrR9KMkikKVdmSBXMAIAAAAABVF2OYjRTGi9Tw8XCAwZWLpX35Yl271TlNWp6N/nROhAVsACAAAAAA0nWwYzXQ1+EkDvnGq+SMlq20z+j32Su+i/A95SggPb4AAzM5AH0AAAAFZAAgAAAAAIy0+bXZi10QC+q7oSOLXK5Fee7VEk/qHSXukfeVIfgzBXMAIAAAAAAQ3IIV/JQCHW95AEbH5zGIHtJqyuPjWPMIZ+VmQHlxEwVsACAAAAAAp0jYsyohKv9Pm+4k+DplEGbl9WLZpAJzitrcDj4CNsMAAzQwAH0AAAAFZAAgAAAAAL5SOJQ3LOhgdXJ5v086NNeAl1qonQnchObdpZJ1kHeEBXMAIAAAAAA+tEqTXODtik+ydJZSnUqXF9f18bPeze9eWtR7ExZJgQVsACAAAAAAbrkZCVgB9Qsp4IAbdf+bD4fT6Boqk5UtuA/zhNrh1y0AAzQxAH0AAAAFZAAgAAAAAKl8zcHJRDjSjJeV/WvMxulW1zrTFtaeBy/aKKhadc6UBXMAIAAAAADBdWQl5SBIvtZZLIHszePwkO14W1mQ0izUk2Ov21cPNAVsACAAAAAAHErCYycpqiIcCZHdmPL1hi+ovLQk4TAvENpfLdTRamQAAzQyAH0AAAAFZAAgAAAAAFvotcNaoKnVt5CBCOPwjexFO0WGWuaIGL6H/6KSau+6BXMAIAAAAAD2y2mBN5xPu5PJoY2zcr0GnQDtHRBogA5+xzIxccE9fwVsACAAAAAAdS34xzJesnUfxLCcc1U7XzUqLy8MAzV/tcjbqaD3lkMAAzQzAH0AAAAFZAAgAAAAAPezU0/vNT4Q4YKbTbaeHqcwNLT+IjW/Y9QFpIooihjPBXMAIAAAAACj2x4O4rHter8ZnTws5LAP9jJ/6kk9C/V3vL50LoFZHAVsACAAAAAAQdBDF3747uCVP5lB/zr8VmzxJfTSZHBKeIgm5FyONXwAAzQ0AH0AAAAFZAAgAAAAAMqpayM2XotEFmm0gwQd9rIzApy0X+7HfOhNk6VU7F5lBXMAIAAAAACJR9+q5T9qFHXFNgGbZnPubG8rkO6cwWhzITQTmd6VgwVsACAAAAAAOZLQ6o7e4mVfDzbpQioa4d3RoTvqwgnbmc5Qh2wsZuoAAzQ1AH0AAAAFZAAgAAAAANCeyW+3oebaQk+aqxNVhAcT/BZ5nhsTVdKS3tMrLSvWBXMAIAAAAADxRFMDhkyuEc++WnndMfoUMLNL7T7rWoeblcrpSI6soQVsACAAAAAAdBuBMJ1lxt0DRq9pOZldQqchLs3B/W02txcMLD490FEAAzQ2AH0AAAAFZAAgAAAAAIbo5YBTxXM7HQhl7UP9NNgpPGFkBx871r1B65G47+K8BXMAIAAAAAC21dJSxnEhnxO5gzN5/34BL4von45e1meW92qowzb8fQVsACAAAAAAm3Hk2cvBN0ANaR5jzeZE5TsdxDvJCTOT1I01X7cNVaYAAzQ3AH0AAAAFZAAgAAAAABm/6pF96j26Jm7z5KkY1y33zcAEXLx2n0DwC03bs/ixBXMAIAAAAAD01OMvTZI/mqMgxIhA5nLs068mW+GKl3OW3ilf2D8+LgVsACAAAAAAaLvJDrqBESTNZSdcXsd+8GXPl8ZkUsGpeYuyYVv/kygAAzQ4AH0AAAAFZAAgAAAAAJ/D3+17gaQdkBqkL2wMwccdmCaVOtxzIkM8VyI4xI5zBXMAIAAAAAAggLVmkc5u+YzBR+oNE+XgLVp64fC6MzUb/Ilu/Jsw0AVsACAAAAAACz3HVKdWkx82/kGbVpcbAeZtsj2R5Zr0dEPfle4IErkAAzQ5AH0AAAAFZAAgAAAAAJMRyUW50oaTzspS6A3TUoXyC3gNYQoShUGPakMmeVZrBXMAIAAAAACona2Pqwt4U2PmFrtmu37jB9kQ/12okyAVtYa8TQkDiQVsACAAAAAAltJJKjCMyBTJ+4PkdDCPJdeX695P8P5h7WOZ+kmExMAAAzUwAH0AAAAFZAAgAAAAAByuYl8dBvfaZ0LO/81JW4hYypeNmvLMaxsIdvqMPrWoBXMAIAAAAABNddwobOUJzm9HOUD8BMZJqkNCUCqstHZkC76FIdNg9AVsACAAAAAAQQOkIQtkyNavqCnhQbNg3HfqrJdsAGaoxSJePJl1qXsAAzUxAH0AAAAFZAAgAAAAAHEzLtfmF/sBcYPPdj8867VmmQyU1xK9I/3Y0478azvABXMAIAAAAAAcmyFajZPnBTbO+oLInNwlApBocUekKkxz2hYFeSlQ+gVsACAAAAAAZ6IkrOVRcC8vSA6Vb4fPWZJrYexXhEabIuYIeXNsCSgAAzUyAH0AAAAFZAAgAAAAAJam7JYsZe2cN20ZYm2W3v1pisNt5PLiniMzymBLWyMtBXMAIAAAAABxCsKVMZMTn3n+R2L7pVz5nW804r8HcK0mCBw3jUXKXAVsACAAAAAA7j3JGnNtR64P4dJLeUoScFRGfa8ekjh3dvhw46sRFk0AAzUzAH0AAAAFZAAgAAAAAMXrXx0saZ+5gORmwM2FLuZG6iuO2YS+1IGPoAtDKoKBBXMAIAAAAADIQsxCr8CfFKaBcx8kIeSywnGh7JHjKRJ9vJd9x79y7wVsACAAAAAAcvBV+SykDYhmRFyVYwFYB9oBKBSHr55Jdz2cXeowsUQAAzU0AH0AAAAFZAAgAAAAACbzcUD3INSnCRspOKF7ubne74OK9L0FTZvi9Ay0JVDYBXMAIAAAAADPebVQH8Btk9rhBIoUOdSAdpPvz7qIY4UC2i6IGisSAQVsACAAAAAAiBunJi0mPnnXdnldiq+If8dcb/n6apHnaIFt+oyYO1kAAzU1AH0AAAAFZAAgAAAAACUc2CtD1MK/UTxtv+8iA9FoHEyTwdl43HKeSwDw2Lp5BXMAIAAAAACCIduIdw65bQMzRYRfjBJj62bc69T4QqH4QoWanwlvowVsACAAAAAAM0TV7S+aPVVzJOQ+cpSNKHTwyQ0mWa8tcHzfk3nR+9IAAzU2AH0AAAAFZAAgAAAAAHSaHWs/dnmI9sc7nB50VB2Bzs0kHapMHCQdyVEYY30TBXMAIAAAAACkV22lhEjWv/9/DubfHBAcwJggKI5mIbSK5L2nyqloqQVsACAAAAAAS19m7DccQxgryOsBJ3GsCs37yfQqNi1G+S6fCXpEhn4AAzU3AH0AAAAFZAAgAAAAAAL8jhNBG0KXXZhmZ0bPXtfgapJCB/AI+BEHB0eZ3C75BXMAIAAAAADHx/fPa639EBmGV5quLi8IQT600ifiKSOhTDOK19DnzwVsACAAAAAAlyLTDVkHxbayklD6Qymh3odIK1JHaOtps4f4HR+PcDgAAzU4AH0AAAAFZAAgAAAAAAxgeclNl09H7HvzD1oLwb2YpFca5eaX90uStYXHilqKBXMAIAAAAACMU5pSxzIzWlQxHyW170Xs9EhD1hURASQk+qkx7K5Y6AVsACAAAAAAJbMMwJfNftA7Xom8Bw/ghuZmSa3x12vTZxBUbV8m888AAzU5AH0AAAAFZAAgAAAAABmO7QD9vxWMmFjIHz13lyOeV6vHT6mYCsWxF7hb/yOjBXMAIAAAAACT9lmgkiqzuWG24afuzYiCeK9gmJqacmxAruIukd0xEAVsACAAAAAAZa0/FI/GkZR7CtX18Xg9Tn9zfxkD0UoaSt+pIO5t1t4AAzYwAH0AAAAFZAAgAAAAAB89SjLtDJkqEghRGyj6aQ/2qvWLNuMROoXmzbYbCMKMBXMAIAAAAAC8sywgND+CjhVTF6HnRQeay8y9/HnVzDI42dEPah28LQVsACAAAAAAoxv7UKh0RqUAWcOsQvU123zO1qZn73Xfib0qncZCB34AAzYxAH0AAAAFZAAgAAAAABN2alGq9Aats1mwERNGwL/fIwZSvVCe9/8XMHTFlpUpBXMAIAAAAACuDPjJgvvbBYhbLpjMiWUCsVppiYrhvR+yMysNPN8cZAVsACAAAAAAKpADjc4bzIZMi9Q/+oe0EMRJHYQt6dlo1x/lRquagqkAAzYyAH0AAAAFZAAgAAAAAL8YB6VAqGBiWD4CBv16IBscg5J7VQCTZu87n6pj+86KBXMAIAAAAAAmxm8e68geeyAdUjSMWBHzUjneVB0pG9TBXIoE6467hAVsACAAAAAAV76JZAlYpgC/Zl8awx2ArCg1uuyy2XVTSkp0wUMi/7UAAzYzAH0AAAAFZAAgAAAAAL4yLkCTV5Dmxa5toBu4JT8ge/cITAaURIOuFuOtFUkeBXMAIAAAAAAXoFNQOMGkAj7qEJP0wQafmFSXgWGeorDVbwyOxWLIsgVsACAAAAAAc4Un6dtIFe+AQ+RSfNWs3q63RTHhmyc+5GKRRdpWRv8AAzY0AH0AAAAFZAAgAAAAAEU8DoUp46YtYjNFS9kNXwdYxQ9IW27vCTb+VcqqfnKNBXMAIAAAAADe7vBOgYReE8X78k5ARuUnv4GmzPZzg6SbConf4L2G3wVsACAAAAAA78YHWVkp6HbZ0zS4UL2z/2pj9vPDcMDt7zTv6NcRsVsAAzY1AH0AAAAFZAAgAAAAAPa4yKTtkUtySuWo1ZQsp2QXtPb5SYqzA5vYDnS1P6c0BXMAIAAAAADKnF58R1sXlHlsHIvCBR3YWW/qk54z9CTDhZydkD1cOQVsACAAAAAAHW3ERalTFWKMzjuXF3nFh0pSrQxM/ojnPbPhc4v5MaQAAzY2AH0AAAAFZAAgAAAAAN5WJnMBmfgpuQPyonmY5X6OdRvuHw4nhsnGRnFAQ95VBXMAIAAAAACwftzu7KVV1rmGKwXtJjs3cJ1gE3apr8+N0SAg1F2cHwVsACAAAAAATDW0reyaCjbJuVLJzbSLx1OBuBoQu+090kgW4RurVacAAzY3AH0AAAAFZAAgAAAAACHvDsaPhoSb6DeGnKQ1QOpGYAgK82qpnqwcmzSeWaJHBXMAIAAAAABRq3C5+dOfnkAHM5Mg5hPB3O4jhwQlBgQWLA7Ph5bhgwVsACAAAAAAqkC8zYASvkVrp0pqmDyFCkPaDmD/ePAJpMuNOCBhni8AAzY4AH0AAAAFZAAgAAAAAOBePJvccPMJmy515KB1AkXF5Pi8NOG4V8psWy0SPRP+BXMAIAAAAAB3dOJG9xIDtEKCRzeNnPS3bFZepMj8UKBobKpSoCPqpgVsACAAAAAAPG3IxQVOdZrr509ggm5FKizWWoZPuVtOgOIGZ3m+pdEAAzY5AH0AAAAFZAAgAAAAABUvRrDQKEXLMdhnzXRdhiL6AGNs2TojPky+YVLXs+JnBXMAIAAAAAD1kYicbEEcPzD4QtuSYQQWDPq8fuUWGddpWayKn3dT9QVsACAAAAAA9+Sf7PbyFcY45hP9oTfjQiOUS3vEIAT8C0vOHymwYSUAAzcwAH0AAAAFZAAgAAAAAOvSnpujeKNen4pqc2HR63C5s5oJ1Vf4CsbKoYQvkwl5BXMAIAAAAACw2+vAMdibzd2YVVNfk81yXkFZP0WLJ82JBxJmXnYE+QVsACAAAAAArQ/E1ACyhK4ZyLqH9mNkCU7WClqRQTGyW9tciSGG/EMAAzcxAH0AAAAFZAAgAAAAAAo0xfGG7tJ3GWhgPVhW5Zn239nTD3PadShCNRc9TwdNBXMAIAAAAADZh243oOhenu0s/P/5KZLBDh9ADqKHtSWcXpO9D2sIjgVsACAAAAAAlgTPaoQKz+saU8rwCT3UiNOdG6hdpjzFx9GBn08ZkBEAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - }, - "limit": 1 - } - ] - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: delete - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDoubleNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-FindOneAndUpdate.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-FindOneAndUpdate.yml deleted file mode 100644 index bea7f48a89..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-FindOneAndUpdate.yml +++ /dev/null @@ -1,912 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoubleNoPrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Double. FindOneAndUpdate." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } - - name: findOneAndUpdate - arguments: - filter: { encryptedDoubleNoPrecision: { $gt: {$numberDouble: "0"}} } - update: { "$set": { "encryptedDoubleNoPrecision": {$numberDouble: "2"}}} - returnDocument: Before - result: *doc1 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - findAndModify: *collection_name - query: { - "encryptedDoubleNoPrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - } - update: { "$set": {"encryptedDoubleNoPrecision": { $$type: "binData" }} } - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: findAndModify - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDoubleNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedDoubleNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "HI88j1zrIsFoijIXKybr9mYubNV5uVeODyLHFH4Ueco=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KhscCh+tt/pp8lxtKZQSPPUU94RvJYPKG/sjtzIa4Ws=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RISnuNrTTVNW5HnwCgQJ301pFw8DOcYrAMQIwVwjOkI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Ra5zukLh2boua0Bh74qA+mtIoixGXlsNsxiJqHtqdTI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "eqr0v+NNWXWszi9ni8qH58Q6gw5x737tJvH3lPaNHO4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "d42QupriWIwGrFAquXNFi0ehEuidIbHLFZtg1Sm2nN8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2azRVxaaTIJKcgY2FU012gcyP8Y05cRDpfUaMnCBaQU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "3nlgkM4K/AAcHesRYYdEu24UGetHodVnVfHzw4yxZBM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hqy91FNmAAac2zUaPO6eWFkx0/37rOWGrwXN+fzL0tU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "akX+fmscSDSF9pB5MPj56iaJPtohr0hfXNk/OPWsGv8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1ZvUb10Q7cN4cNLktd5yNjqgtawsYnkbeVBZV6WuY/I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "otCwtuKiY4hCyXvYzXvo10OcnzZppebo38KsAlq49QM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Mty8EscckeT/dhMfrPFyDbLnmMOcYRUQ3mLK4KTu6V8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tnvgLLkJINO7csREYu4dEVe1ICrBeu7OP+HdfoX3M2E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kOefsHgEVhkJ17UuP7Dxogy6sAQbzf1SFPKCj6XRlrQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "F+JQ79xavpaHdJzdhvwyHbzdZJLNHAymc/+67La3gao=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "NCZ9zp5rDRceENuSgAfTLEyKg0YgmXAhK0B8WSj7+Pw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wL1CJ7cYR5slx8mHq++uMdjDfkt9037lQTUztEMF56M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "txefkzTMITZE+XvvRFZ7QcgwDT/7m8jNmxRk4QBaoZI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jFunW3v1tSYMyZtQQD28eEy9qqDp4Kqo7gMN29N4bfQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QMO915KUiS3X3R1bU1YoafVM2s0NeHo3EjgTA9PnGwY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "nwdKJEXdilzvb7494vbuDJ+y6SrfJahza1dYIsHIWVI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vpWMX+T/VXXajFo0UbuYjtp0AEzBU0Y+lP+ih2EQ7mg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1lmzG0J1DhKDRhhq5y5Buygu4G8eV2X0t7kUY90EohM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SiKqpXqO0trwhFvBWK274hMklpCgMhNs/JY84yyn/NE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7cPGPYCKPTay+ZR9Gx6oOueduOgaFrSuAXmNDpDHXdI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4THEYvAkjs2Fh7FIe5LC45P4i4N0L7ob67UOVbhp6Nk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "B+UGsChLLZR7iqnt8yq91OgmTgwiUKTJhFxY4NT0O6c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X1uYwBCsCg1H+PnKdwtBqXlt0zKEURi8bOM940GcPfk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xYOgT5l7shlNXCwHlguovmDkcEnF8dXyYlTyYrgZ8GE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vFMTZqV8bh1+gcKzTkXweMddJlgdUnwX0DWzUUaMok4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4HI0y9FrtleZxZ7M6INdNhLelrQ2Rv/+ykWCBl+tMC8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jpJ0bBE474OUkn1vUiLWumIBtYmwc7J5+LQU/nyeLQc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jQTPeXZvdxY/DjtPfYfKUArIDsf0E9MVFy2O26sv1ec=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QLLto0ExR2ZYMGqlyaMZc/hXFFTlwmgtKbiVq/xJIeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yBJNviU1nchbGbhx6InXCVRXa90sEepz1EwbYuKXu2U=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jpEf0vHxrPu9gTJutNXSi2g/2Mc4WXFEN7yHonZEb7A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "E09kLFckMYwNuhggMxmPtwndyvIAx+Vl+b2CV6FP75s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "N+ue6/cLPb5NssmJCCeo18LlbKPz6r2z20AsnTKRvOo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yVQNZP8hhsvNGyDph2QP2qTNdXZTiIEVineKg+Qf33o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cSC9uI+9c5S8X+0G7amVyug1p0ZlgBsbEDYYyezBevQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1NpZGjoQzuQtekj80Rifxe9HbE08W07dfwxaFHaVn84=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "5Ghuq/8l11Ug9Uf/RTwf9On3OxOwIXUcb9soiy4J7/w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0LWKaEty6ywxLFhDaAqulqfMnYc+tgPfH4apyEeKg80=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OwSthmCBtt6NIAoAh7aCbj82Yr/+9t8U7WuBQhFT3AQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "iYiyg6/1isqbMdvFPIGucu3cNM4NAZNtJhHpGZ4eM+c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "waBgs8jWuGJPIF5zCRh6OmIyfK5GCBQgTMfmKSR2wyY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1Jdtbe2BKJXPU2G9ywOrlODZ/cNYEQlKzAW3aMe1Hy4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xaLEnNUS/2ySerBpb9dN/D31t+wYcKekwTfkwtni0Mc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "bIVBrOhOvr6cL55Tr24+B+CC9MiG7U6K54aAr2IXXuw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6Cdq5wroGu2TEFnekuT7LhOpd/K/+PcipIljcHU9QL4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "K5l64vI4S/pLviLW6Pl0U3iQkI3ge0xg4RAHcEsyKJo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "bzhuvZ0Ls22yIOX+Hz51eAHlSuDbWR/e0u4EhfdpHbc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Qv+fr6uD4o0bZRp69QJCFL6zvn3G82c7L+N1IFzj7H0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XAmISMbD3aEyQT+BQEphCKFNa0F0GDKFuhM9cGceKoQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4VLCokntMfm1AogpUnYGvhV7nllWSo3mS3hVESMy+hA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xiXNLj/CipEH63Vb5cidi8q9X47EF4f3HtJSOH7mfM8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4XlCYfYBjI9XA5zOSgTiEBYcZsdwyXL+f5XtH2xUIOc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "k6DfQy7ZYJIkEly2B5hjOZznL4NcgMkllZjJLb7yq7w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ZzM6gwWesa3lxbZVZthpPFs2s3GV0RZREE2zOMhBRBo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "US+jeMeeOd7J0wR0efJtq2/18lcO8YFvhT4O3DeaonQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "b6iSxiI1FM9SzxuG1bHqGA1i4+3GOi0/SPW00XB4L7o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kn3LsxAVkzIZKK9I6fi0Cctr0yjXOYgaQWMCoj4hLpM=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-InsertFind.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-InsertFind.yml deleted file mode 100644 index f99b4c5ac9..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-InsertFind.yml +++ /dev/null @@ -1,908 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoubleNoPrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Double. Insert and Find." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } - - name: find - arguments: - filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0" } } } - result: [*doc1] - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - find: *collection_name - filter: - "encryptedDoubleNoPrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: find - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDoubleNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedDoubleNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2FIZh/9N+NeJEQwxYIX5ikQT85xJzulBNReXk8PnG/s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FWXI/yZ1M+2fIboeMCDMlp+I2NwPQDtoM/wWselOPYw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uk26nvN/LdRLaBphiBgIZzT0sSpoO1z0RdDWRm/xrSA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hiiYSH1KZovAULc7rlmEU74wCjzDR+mm6ZnsgvFQjMw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hRzvMvWPX0sJme+wck67lwbKDFaWOa+Eyef+JSdc1s4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PSx5D+zqC9c295dguX4+EobT4IEzfffdfjzC8DWpB5Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QzfXQCVTjPQv2h21v95HYPq8uCsVJ2tPnjv79gAaM9M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XcGDO/dlTcEMLqwcm55UmOqK+KpBmbzZO1LIzX7GPaQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Lf+o4E7YB5ynzUPC6KTyW0lj6Cg9oLIu1Sdd1ODHctA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wAuVn02LAVo5Y+TUocvkoenFYWzpu38k0NmGZOsAjS4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yJGDtveLbbo/0HtCtiTSsvVI/0agg/U1bFaQ0yhK12o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KsEy0zgYcmkM+O/fWF9z3aJGIk22XCk+Aw96HB6JU68=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "p+AnMI5ZxdJMSIEJmXXya+FeH5yubmOdViwUO89j0Rc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/jLix56jzeywBtNuGw55lCXyebQoSIhbful0hOKxKDY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fvDvSPomtJsl1S3+8/tzFCE8scHIdJY5hB9CdTEsoFo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "oV5hOJzPXxfTuRdKIlF4uYEoMDuqH+G7/3qgndDr0PM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "3ALwcvLj3VOfgD6OqXAO13h1ZkOv46R6+Oy6SUKh53I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gxaB9FJj0IM+InhvAjwWaex3UIZ9SAnDiUd5WHSY/l0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "66NPvDygJzKJqddfNuDuNOpvGajjFRtvhkwfUkiYmXw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1dWcQIocRAcO9XnXYqbhl83jc0RgjQpsrWd8dC27trg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "npos0Uf1DT3ztSCjPVY9EImlRnTHB1KLrvmVSqBQ/8E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "TEI9qBx/tK1l1H0v1scMG8Srmtwo5VxWHADPBSlWrXk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "3wUN2ypQKoj+5ASkeIK9ycxhahVxyTmGopigoUAlyYs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "o/oksSnUS+nIq6ozWTbB5bJh+NoaPj8deAA23uxiWCk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KExYPruhA31e8xuSwvfUfDcyY/H2Va6taUd0k4yFgLc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "/x+dNfxdd/lkx8Z8VZVfoYl7LPoaZ/iKEzZXBrAtIJc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DE4cmjFLPqZlmRomO0qQiruUBtzoCe8ZdNRcfNH92pU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "M6EKNcLPw/iojAChgYUSieaBYWcbsjKtB94SaHOr8vk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+qP49lDPeyhaduTvXJgtJEqHNEYANVu9Bg3Bxz7Td9w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ruMrC2VIS+VKbJwCFb3bfkaLTju9nE+yPONV9s0M0Vo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EbjDlSB5JKnDKff4d8hOmaOwJ7B9Q6NQFisLj+DPC+0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "C/yYOTB94edyqAbiQNu8/H7FoG3yRRjHDkMykz4+Mv0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CBxqrejG+qQQq2YTd6iP/06kiu2CxxzBFaZK3Ofb1CM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2ZOQ/fpho+AbDENWBZaln7wRoepIRdhyT648dr8O5cU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "EghIgEPz01+myPgj8oid+PgncvobvC7vjvG3THEEQ0M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "92CysZYNF8riwAMhdrIPKxfODw9p07cKQy/Snn8XmVY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VO0LeTBQmsEf7sCHzTnZwUPNTqRZ49R8V5E9XnZ/5N4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "exs8BQMJq7U6ZXYgIizT7XN+X/hOmmn4YEuzev9zgSI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qHpS4k1I+gPniNp4CA8TY8lLN36vBYmgbKMFpbYMEqg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+7lWKCKAWFw6gPZdHE6E8KIfI14/fSvtWUmllb5WLi0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YiH/US0q6679hWblFDDKNqUjCgggoU8sUCssTIF1QbU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YgwkKElEubNfvXL9hJxzqQUQtHiXN/OCGxNL1MUZZlM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hZFST4INZTTuhvJlGJeMwlUAK270UCOTCDeBAnN4a7g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "24I1Zw35AuGnK3CqJhbCwYb0IPuu5sCRrM5iyeITOLc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vgD12JB4Q1S/kGPSQ1KOgp386KnG1GbM/5+60oRGcGw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+wNE+OL+CB9d4AUJdVxd56jUJCAXmmk9fapuB2TAc4g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uhQh1B2Pe4RkNw/kPEcgaLenuikKoRf1iyfZhpXdodc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "eu8gjAUIp8ybO204AgeOq5v1neI1yljqy5v3I6lo1lM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7QG6oVbASBAjrnCPxzzUNnuFSFNlKhbuBafkF8pr7Is=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "PUS1xb2oHSDTdYltutoSSxBiJ1NjxH3l2kA4P1CZLEs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XPMh/JDC/O93gJJCwwgJDb8ssWZvRvezNmKmyn3nIfk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jWz+KGwMk/GOvFAK2rOxF3OjxeZAWfmUQ1HGJ7icw4A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "o7XbW68pc6flYigf3LW4WAGUWxpeqxaQLkHUhUR9RZ8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "nqR+g60+5U0okbqJadSqGgnC+j1JcP8rwMcfzOs2ACI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Hz43qVK95tSfbYFtaE/8fE97XMk1RiO8XpWjwZHB80o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "noZUWlZ8M6KXU5rkifyo8/duw5IL7/fXbJvT7bNmW9k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WONVHCuPSanXDRQQ/3tmyJ0Vq+Lu/4hRaMUf0g0kSuw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UEaj6vQRoIghE8Movd8AGXhtwIOXlP4cBsECIUvE5Y8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "D3n2YcO8+PB4C8brDo7kxKjF9Y844rVkdRMLTgsQkrw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "C+YA0G9KjxZVaWwOMuh/dcnHnHAlYnbFrRl0IEpmsY0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rUnmbmQanxrbFPYYrwyQ53x66OSt27yAvF+s48ezKDc=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Update.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Update.yml deleted file mode 100644 index 8f31c0cdbf..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Update.yml +++ /dev/null @@ -1,925 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoubleNoPrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Double. Update." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } - - name: updateOne - arguments: - filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0" } } } - update: { "$set": { "encryptedDoubleNoPrecision": { $numberDouble: "2" } }} - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command_name: update - command: - "update": "default" - "ordered": true - "updates": [ - { - "q": { - "encryptedDoubleNoPrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - }, - "u": { - "$set": { - "encryptedDoubleNoPrecision": { $$type: "binData" } - } - } - } - ] - encryptionInformation: - type: 1 - schema: - "default.default": - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - "$db": "default" - - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDoubleNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedDoubleNoPrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "HI88j1zrIsFoijIXKybr9mYubNV5uVeODyLHFH4Ueco=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "KhscCh+tt/pp8lxtKZQSPPUU94RvJYPKG/sjtzIa4Ws=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RISnuNrTTVNW5HnwCgQJ301pFw8DOcYrAMQIwVwjOkI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Ra5zukLh2boua0Bh74qA+mtIoixGXlsNsxiJqHtqdTI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "eqr0v+NNWXWszi9ni8qH58Q6gw5x737tJvH3lPaNHO4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "d42QupriWIwGrFAquXNFi0ehEuidIbHLFZtg1Sm2nN8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "2azRVxaaTIJKcgY2FU012gcyP8Y05cRDpfUaMnCBaQU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "3nlgkM4K/AAcHesRYYdEu24UGetHodVnVfHzw4yxZBM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hqy91FNmAAac2zUaPO6eWFkx0/37rOWGrwXN+fzL0tU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "akX+fmscSDSF9pB5MPj56iaJPtohr0hfXNk/OPWsGv8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1ZvUb10Q7cN4cNLktd5yNjqgtawsYnkbeVBZV6WuY/I=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "otCwtuKiY4hCyXvYzXvo10OcnzZppebo38KsAlq49QM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Mty8EscckeT/dhMfrPFyDbLnmMOcYRUQ3mLK4KTu6V8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "tnvgLLkJINO7csREYu4dEVe1ICrBeu7OP+HdfoX3M2E=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kOefsHgEVhkJ17UuP7Dxogy6sAQbzf1SFPKCj6XRlrQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "F+JQ79xavpaHdJzdhvwyHbzdZJLNHAymc/+67La3gao=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "NCZ9zp5rDRceENuSgAfTLEyKg0YgmXAhK0B8WSj7+Pw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wL1CJ7cYR5slx8mHq++uMdjDfkt9037lQTUztEMF56M=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "txefkzTMITZE+XvvRFZ7QcgwDT/7m8jNmxRk4QBaoZI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jFunW3v1tSYMyZtQQD28eEy9qqDp4Kqo7gMN29N4bfQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QMO915KUiS3X3R1bU1YoafVM2s0NeHo3EjgTA9PnGwY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "nwdKJEXdilzvb7494vbuDJ+y6SrfJahza1dYIsHIWVI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vpWMX+T/VXXajFo0UbuYjtp0AEzBU0Y+lP+ih2EQ7mg=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1lmzG0J1DhKDRhhq5y5Buygu4G8eV2X0t7kUY90EohM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SiKqpXqO0trwhFvBWK274hMklpCgMhNs/JY84yyn/NE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7cPGPYCKPTay+ZR9Gx6oOueduOgaFrSuAXmNDpDHXdI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4THEYvAkjs2Fh7FIe5LC45P4i4N0L7ob67UOVbhp6Nk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "B+UGsChLLZR7iqnt8yq91OgmTgwiUKTJhFxY4NT0O6c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X1uYwBCsCg1H+PnKdwtBqXlt0zKEURi8bOM940GcPfk=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xYOgT5l7shlNXCwHlguovmDkcEnF8dXyYlTyYrgZ8GE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "vFMTZqV8bh1+gcKzTkXweMddJlgdUnwX0DWzUUaMok4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4HI0y9FrtleZxZ7M6INdNhLelrQ2Rv/+ykWCBl+tMC8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jpJ0bBE474OUkn1vUiLWumIBtYmwc7J5+LQU/nyeLQc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jQTPeXZvdxY/DjtPfYfKUArIDsf0E9MVFy2O26sv1ec=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "QLLto0ExR2ZYMGqlyaMZc/hXFFTlwmgtKbiVq/xJIeI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yBJNviU1nchbGbhx6InXCVRXa90sEepz1EwbYuKXu2U=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jpEf0vHxrPu9gTJutNXSi2g/2Mc4WXFEN7yHonZEb7A=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "E09kLFckMYwNuhggMxmPtwndyvIAx+Vl+b2CV6FP75s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "N+ue6/cLPb5NssmJCCeo18LlbKPz6r2z20AsnTKRvOo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "yVQNZP8hhsvNGyDph2QP2qTNdXZTiIEVineKg+Qf33o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cSC9uI+9c5S8X+0G7amVyug1p0ZlgBsbEDYYyezBevQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1NpZGjoQzuQtekj80Rifxe9HbE08W07dfwxaFHaVn84=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "5Ghuq/8l11Ug9Uf/RTwf9On3OxOwIXUcb9soiy4J7/w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0LWKaEty6ywxLFhDaAqulqfMnYc+tgPfH4apyEeKg80=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "OwSthmCBtt6NIAoAh7aCbj82Yr/+9t8U7WuBQhFT3AQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "iYiyg6/1isqbMdvFPIGucu3cNM4NAZNtJhHpGZ4eM+c=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "waBgs8jWuGJPIF5zCRh6OmIyfK5GCBQgTMfmKSR2wyY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "1Jdtbe2BKJXPU2G9ywOrlODZ/cNYEQlKzAW3aMe1Hy4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xaLEnNUS/2ySerBpb9dN/D31t+wYcKekwTfkwtni0Mc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "bIVBrOhOvr6cL55Tr24+B+CC9MiG7U6K54aAr2IXXuw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6Cdq5wroGu2TEFnekuT7LhOpd/K/+PcipIljcHU9QL4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "K5l64vI4S/pLviLW6Pl0U3iQkI3ge0xg4RAHcEsyKJo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "bzhuvZ0Ls22yIOX+Hz51eAHlSuDbWR/e0u4EhfdpHbc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Qv+fr6uD4o0bZRp69QJCFL6zvn3G82c7L+N1IFzj7H0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "XAmISMbD3aEyQT+BQEphCKFNa0F0GDKFuhM9cGceKoQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4VLCokntMfm1AogpUnYGvhV7nllWSo3mS3hVESMy+hA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "xiXNLj/CipEH63Vb5cidi8q9X47EF4f3HtJSOH7mfM8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "4XlCYfYBjI9XA5zOSgTiEBYcZsdwyXL+f5XtH2xUIOc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "k6DfQy7ZYJIkEly2B5hjOZznL4NcgMkllZjJLb7yq7w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ZzM6gwWesa3lxbZVZthpPFs2s3GV0RZREE2zOMhBRBo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "US+jeMeeOd7J0wR0efJtq2/18lcO8YFvhT4O3DeaonQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "b6iSxiI1FM9SzxuG1bHqGA1i4+3GOi0/SPW00XB4L7o=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kn3LsxAVkzIZKK9I6fi0Cctr0yjXOYgaQWMCoj4hLpM=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Aggregate.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Aggregate.yml deleted file mode 100644 index e70e2d17fb..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Aggregate.yml +++ /dev/null @@ -1,326 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDouble': '0.0'}, 'max': {'$numberDouble': '200.0'}, 'precision': {'$numberInt': '2'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range DoublePrecision. Aggregate." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } - - name: aggregate - arguments: - pipeline: [{ $match: { "encryptedDoublePrecision": { $gt: {$numberDouble: "0" }} } }] - result: [*doc1] - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - aggregate: *collection_name - pipeline: [ - { - "$match": { - "encryptedDoublePrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - } - } - ] - cursor: {} - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: aggregate - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDoublePrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedDoublePrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Correctness.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Correctness.yml deleted file mode 100644 index 4d7842a912..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Correctness.yml +++ /dev/null @@ -1,425 +0,0 @@ -# Test correctness results. -# Does not include command monitoring expectations or outcome assertions to make tests more readable. - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDouble': '0.0'}, 'max': {'$numberDouble': '200.0'}, 'precision': {'$numberInt': '2'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "Find with $gt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0.0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1.0" } } - - name: find - arguments: - filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0.0" } }} - result: [*doc1] - - - description: "Find with $gte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoublePrecision: { $gte: { $numberDouble: "0.0" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $gt with no results" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "1.0" } }} - result: [] - - - description: "Find with $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoublePrecision: { $lt: { $numberDouble: "1.0" } }} - result: [*doc0] - - - description: "Find with $lte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoublePrecision: { $lte: { $numberDouble: "1.0" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $lt below min" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoublePrecision: { $lt: { $numberDouble: "0.0" } }} - result: - errorContains: must be greater than the range minimum - - - description: "Find with $gt above max" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "200.0" } }} - result: - errorContains: must be less than the range max - - - description: "Find with $gt and $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} - result: [*doc1] - - - description: "Find with equality" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoublePrecision: { $numberDouble: "0.0" } } - result: [*doc0] - - name: find - arguments: - filter: { encryptedDoublePrecision: { $numberDouble: "1.0" } } - result: [*doc1] - - - description: "Find with full range" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoublePrecision: { $gte: {$numberDouble: "0.0"}, $lte: {$numberDouble: "200.0"} } } - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $in" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedDoublePrecision: { $in: [ {$numberDouble: "0.0"} ] } } - result: [*doc0] - - - description: "Insert out of range" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: { _id: 0, encryptedDoublePrecision: { $numberDouble: "-1" }} - result: - errorContains: value must be greater than or equal to the minimum value - - - description: "Insert min and max" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: *doc0 - - name: insertOne - arguments: - document: &doc200 { _id: 200, encryptedDoublePrecision: { $numberDouble: "200.0" }} - - name: find - arguments: - filter: {} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc200] - - - description: "Aggregate with $gte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoublePrecision: { $gte: { $numberDouble: "0.0" } }} } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $gt with no results" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoublePrecision: { $gt: { $numberDouble: "1.0" } }} } - result: [] - - - description: "Aggregate with $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoublePrecision: { $lt: { $numberDouble: "1.0" } }} } - result: [*doc0] - - - description: "Aggregate with $lte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoublePrecision: { $lte: { $numberDouble: "1.0" } }} } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $lt below min" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoublePrecision: { $lt: { $numberDouble: "0.0" } }} } - result: - errorContains: must be greater than the range minimum - - - description: "Aggregate with $gt above max" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoublePrecision: { $gt: { $numberDouble: "200.0" } }} } - result: - errorContains: must be less than the range max - - - description: "Aggregate with $gt and $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoublePrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} } - result: [*doc1] - - - description: "Aggregate with equality" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoublePrecision: { $numberDouble: "0.0" } } } - result: [*doc0] - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoublePrecision: { $numberDouble: "1.0" } } } - result: [*doc1] - - - description: "Aggregate with full range" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoublePrecision: { $gte: {$numberDouble: "0.0"}, $lte: {$numberDouble: "200.0"} } } } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $in" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedDoublePrecision: { $in: [ {$numberDouble: "0.0"} ] } } } - result: [*doc0] - - - description: "Wrong type: Insert Int" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: { _id: 0, encryptedDoublePrecision: { $numberInt: "0" }} } - result: - # Expect an error from mongocryptd. - errorContains: "cannot encrypt element" - - - description: "Wrong type: Find Int" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: find - arguments: - filter: { encryptedDoublePrecision: { $gte: { $numberInt: "0" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: - # expect an error from libmongocrypt. - errorContains: "field type is not supported" \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Delete.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Delete.yml deleted file mode 100644 index 226190acc9..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Delete.yml +++ /dev/null @@ -1,225 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDouble': '0.0'}, 'max': {'$numberDouble': '200.0'}, 'precision': {'$numberInt': '2'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range DoublePrecision. Delete." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } - - name: deleteOne - arguments: - filter: { "encryptedDoublePrecision": { $gt: {$numberDouble: "0" }} } - result: - deletedCount: 1 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - delete: *collection_name - deletes: [ - { - "q": { - "encryptedDoublePrecision": { - "$gt": { - "$binary": { - "base64": "DdIJAAADcGF5bG9hZACiCQAABGcAjgkAAAMwAH0AAAAFZAAgAAAAAHdJ2Vnb4MMzqVYVssjSdDy8XU4GVzMTfGifGETgQ2mYBXMAIAAAAAD7cFfKJGIXo6PjyeX2ria02CckW7dWFDoY/3FyBdm1NQVsACAAAAAAhEPSNv4M023A3hzNFuy83+hIKuZ2mKRY954N++aEOBUAAzEAfQAAAAVkACAAAAAAlmvfDrZoydUet4eCVMq7z6a58Ea+1HLJOWxN5lNcrWEFcwAgAAAAAEBo5AWZyC41b9ayjWNQSL4iYEAIwR/JG+ssN8bdoK9RBWwAIAAAAACEndE0SLxFSElOrNnqeX0EPmgDio3udZjVREy4JLS3sQADMgB9AAAABWQAIAAAAABbiLaoxAA6rinMJw1hC8ZUiq6UU1AQaPFn/py/Y06WuQVzACAAAAAAhtDasFkvYE7SCNu1je/hxdE9TJtAvvH3NtdEbKzNbCUFbAAgAAAAAIGepU1RSCF8sWODHEpKglsoqw3VBBH4a/URGxgGzbq2AAMzAH0AAAAFZAAgAAAAALORWwSr+tYNxcil2KIGSbNhTHvcPbdj+rLVQNx21S/KBXMAIAAAAAD6diZBkPEJ1cQy06LAxdbNK8Nlxbb44fH4Wk3Y3260nQVsACAAAAAA1eYAZBFHlDiaDAljWi8blGQ2nvvZa5AO5doeo0SFZsgAAzQAfQAAAAVkACAAAAAAG5XMK96PjClNlUvg82j4pMY1YxsznZfj4uNweD394FoFcwAgAAAAAKHgQLdGJHkrfFg9nB93Ac+3VgBw6aU44MTkKIQ91dZoBWwAIAAAAAAPxXmi+SDJ+40A0KdwfRczexlZQrHjIA+D3oUB0EY9tAADNQB9AAAABWQAIAAAAAA6M++b9I0YFemmWBAWAE3glu2Ah3Ta1FBxAQEIWS0toAVzACAAAAAANXYTqPf1Y6X3Ns6YQIX0C3FKCyWUo+Kk+fNcQvc0WSoFbAAgAAAAAA+uJUw1ICYgyeygSRe206VTWVtUnhdci3iHbyP5YtEVAAM2AH0AAAAFZAAgAAAAAKl8bV1riH/uyJ+X0HHd3+18k2cJl2dQFXCdoagutFcaBXMAIAAAAABm8F2Ew9f0VOABdcF+lP0Bi+zWvEUPniWgrxPq/Sx3uwVsACAAAAAAJfFErjZ6BPhsw5LjJLqNtKDLJ4zV0eIZppQpd9b0wZoAAzcAfQAAAAVkACAAAAAAsYZD8JEP6kYsPncFnNZwJxhu4YtUTKPNcjHtv67H+rYFcwAgAAAAAI4LqZcRkvbs/2F62Flu0pixNcor4WmBD0DHGaf039wLBWwAIAAAAAD4wUR3xd9lKltcqqo8LYvdMQWzCRobkV/ppKB/yn5dUgADOAB9AAAABWQAIAAAAAC0vdAi+dmoIXvZ5LqUqvyKV9/tHqSI2SWiSJO5pTnA2wVzACAAAAAAS2qvf9fvfVUH5WtsVxjxmskpGjYTQV34LwvQQw1y9wIFbAAgAAAAAE0+FKuK7HxbypvCeEJzMTcjOWE0ScYOlTBMUNlIv55hAAM5AH0AAAAFZAAgAAAAAH31lb/srBcrOXkzddCwAnclsR5/3QijEVgECs2JjOWBBXMAIAAAAABg7+prDT73YcCvLE5QbuIrqGcjLc5pQD2Miq0d29yrxgVsACAAAAAAetRiPwDSFWBzpWSWkOKWM6fKStRJ8SyObnpc79ux8p0AAzEwAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzExAH0AAAAFZAAgAAAAAFdthRhe2Q8CvxGIhjTJZv0Lk97GkHciTPxZ/mckLoNaBXMAIAAAAAAqOxsAr23LOVB0DIHbPf9UDJJRFXY2YoKbjhRqw5psbQVsACAAAAAA0G2GD8ZQjDBntjLpW4rqwKRS6HiUjL03g1N6chANozcAAzEyAH0AAAAFZAAgAAAAAMWymwwbvIeMqmnKWWifUqoCxOsdpnonM2qdLPyjqJO/BXMAIAAAAAB6IDmmpUhBD2zpRj8/y/kmOSXcjuIU14sNh6GKSsg2uwVsACAAAAAAWMFPNOk3EMSQDS9JGPSMIQP0oNGVugxXKKUrIPPlhHgAAzEzAH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzE0AH0AAAAFZAAgAAAAAJaRYmo8zqI2BEUzdSwp4tVRpPmVWsfydkYN3UHh6TMuBXMAIAAAAAAeD6mDnQeLlbC9i0sVgE8+RH6y+e94OJQ0tJ0PvblVSgVsACAAAAAAWp4jvretbDEsqEMzP/WLTnwOiJwCtfrCiB6m8k+yEMoAAzE1AH0AAAAFZAAgAAAAAAZZ538coNPwyRjhEwr5P8Xw32oWOJF+R+nfCGgy2qO3BXMAIAAAAACOPLnJlKwGNPDBReRKnHfteq0wFb3ezhrc7BVXs8RUHwVsACAAAAAA+lGesNk3+SyB/60rSvdQ2aN2vfJPR7llJVhufGTNhHkAAzE2AH0AAAAFZAAgAAAAAFH9l9GGA1I52atJV5jNUf1lx8jBjoEoVoME97v5GFJiBXMAIAAAAAC1qH3Kd78Dr9NGbw7y9D/XYBwv5h1LLO8la5OU7g8UkQVsACAAAAAArZ6atJCYrVfHB8dSNPOFf6nnDADBMJcIEj8ljPvxHp8AAzE3AH0AAAAFZAAgAAAAADtbVEI2tdkrowEMdkacD2w0Y3T3Ofi7PH6HmA6sP0c/BXMAIAAAAADuBSROnZHA+NgUPH8d0LnWFiDsM2bY8bzjC1+elSsIygVsACAAAAAAR0G2m+uANoWknkr/NerFcG+fECVxNIs0cqbY1t/U/0MAAzE4AH0AAAAFZAAgAAAAAAh3WpeMVlikPFYj9hLj+fmIqVt6omCSF75W3TPExyWpBXMAIAAAAAAsQkRmwqeVj2gGE03orb6PtrIzDt6dDU3hgSQi8E2wKgVsACAAAAAA3GHaRE2RAcaBRd8VzmYzWeBD2Gmy91eTK1k8YdWObZcAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - }, - "limit": 1 - } - ] - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: delete - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDoublePrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-FindOneAndUpdate.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-FindOneAndUpdate.yml deleted file mode 100644 index dae9c4bdaa..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-FindOneAndUpdate.yml +++ /dev/null @@ -1,324 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDouble': '0.0'}, 'max': {'$numberDouble': '200.0'}, 'precision': {'$numberInt': '2'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range DoublePrecision. FindOneAndUpdate." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } - - name: findOneAndUpdate - arguments: - filter: { encryptedDoublePrecision: { $gt: {$numberDouble: "0"}} } - update: { "$set": { "encryptedDoublePrecision": {$numberDouble: "2"}}} - returnDocument: Before - result: *doc1 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - findAndModify: *collection_name - query: { - "encryptedDoublePrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - } - update: { "$set": {"encryptedDoublePrecision": { $$type: "binData" }} } - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: findAndModify - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDoublePrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedDoublePrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-InsertFind.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-InsertFind.yml deleted file mode 100644 index 2f6dda3cad..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-InsertFind.yml +++ /dev/null @@ -1,320 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDouble': '0.0'}, 'max': {'$numberDouble': '200.0'}, 'precision': {'$numberInt': '2'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range DoublePrecision. Insert and Find." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } - - name: find - arguments: - filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0" } } } - result: [*doc1] - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - find: *collection_name - filter: - "encryptedDoublePrecision": { - "$gt": { - "$binary": { - "base64": "DdIJAAADcGF5bG9hZACiCQAABGcAjgkAAAMwAH0AAAAFZAAgAAAAAHdJ2Vnb4MMzqVYVssjSdDy8XU4GVzMTfGifGETgQ2mYBXMAIAAAAAD7cFfKJGIXo6PjyeX2ria02CckW7dWFDoY/3FyBdm1NQVsACAAAAAAhEPSNv4M023A3hzNFuy83+hIKuZ2mKRY954N++aEOBUAAzEAfQAAAAVkACAAAAAAlmvfDrZoydUet4eCVMq7z6a58Ea+1HLJOWxN5lNcrWEFcwAgAAAAAEBo5AWZyC41b9ayjWNQSL4iYEAIwR/JG+ssN8bdoK9RBWwAIAAAAACEndE0SLxFSElOrNnqeX0EPmgDio3udZjVREy4JLS3sQADMgB9AAAABWQAIAAAAABbiLaoxAA6rinMJw1hC8ZUiq6UU1AQaPFn/py/Y06WuQVzACAAAAAAhtDasFkvYE7SCNu1je/hxdE9TJtAvvH3NtdEbKzNbCUFbAAgAAAAAIGepU1RSCF8sWODHEpKglsoqw3VBBH4a/URGxgGzbq2AAMzAH0AAAAFZAAgAAAAALORWwSr+tYNxcil2KIGSbNhTHvcPbdj+rLVQNx21S/KBXMAIAAAAAD6diZBkPEJ1cQy06LAxdbNK8Nlxbb44fH4Wk3Y3260nQVsACAAAAAA1eYAZBFHlDiaDAljWi8blGQ2nvvZa5AO5doeo0SFZsgAAzQAfQAAAAVkACAAAAAAG5XMK96PjClNlUvg82j4pMY1YxsznZfj4uNweD394FoFcwAgAAAAAKHgQLdGJHkrfFg9nB93Ac+3VgBw6aU44MTkKIQ91dZoBWwAIAAAAAAPxXmi+SDJ+40A0KdwfRczexlZQrHjIA+D3oUB0EY9tAADNQB9AAAABWQAIAAAAAA6M++b9I0YFemmWBAWAE3glu2Ah3Ta1FBxAQEIWS0toAVzACAAAAAANXYTqPf1Y6X3Ns6YQIX0C3FKCyWUo+Kk+fNcQvc0WSoFbAAgAAAAAA+uJUw1ICYgyeygSRe206VTWVtUnhdci3iHbyP5YtEVAAM2AH0AAAAFZAAgAAAAAKl8bV1riH/uyJ+X0HHd3+18k2cJl2dQFXCdoagutFcaBXMAIAAAAABm8F2Ew9f0VOABdcF+lP0Bi+zWvEUPniWgrxPq/Sx3uwVsACAAAAAAJfFErjZ6BPhsw5LjJLqNtKDLJ4zV0eIZppQpd9b0wZoAAzcAfQAAAAVkACAAAAAAsYZD8JEP6kYsPncFnNZwJxhu4YtUTKPNcjHtv67H+rYFcwAgAAAAAI4LqZcRkvbs/2F62Flu0pixNcor4WmBD0DHGaf039wLBWwAIAAAAAD4wUR3xd9lKltcqqo8LYvdMQWzCRobkV/ppKB/yn5dUgADOAB9AAAABWQAIAAAAAC0vdAi+dmoIXvZ5LqUqvyKV9/tHqSI2SWiSJO5pTnA2wVzACAAAAAAS2qvf9fvfVUH5WtsVxjxmskpGjYTQV34LwvQQw1y9wIFbAAgAAAAAE0+FKuK7HxbypvCeEJzMTcjOWE0ScYOlTBMUNlIv55hAAM5AH0AAAAFZAAgAAAAAH31lb/srBcrOXkzddCwAnclsR5/3QijEVgECs2JjOWBBXMAIAAAAABg7+prDT73YcCvLE5QbuIrqGcjLc5pQD2Miq0d29yrxgVsACAAAAAAetRiPwDSFWBzpWSWkOKWM6fKStRJ8SyObnpc79ux8p0AAzEwAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzExAH0AAAAFZAAgAAAAAFdthRhe2Q8CvxGIhjTJZv0Lk97GkHciTPxZ/mckLoNaBXMAIAAAAAAqOxsAr23LOVB0DIHbPf9UDJJRFXY2YoKbjhRqw5psbQVsACAAAAAA0G2GD8ZQjDBntjLpW4rqwKRS6HiUjL03g1N6chANozcAAzEyAH0AAAAFZAAgAAAAAMWymwwbvIeMqmnKWWifUqoCxOsdpnonM2qdLPyjqJO/BXMAIAAAAAB6IDmmpUhBD2zpRj8/y/kmOSXcjuIU14sNh6GKSsg2uwVsACAAAAAAWMFPNOk3EMSQDS9JGPSMIQP0oNGVugxXKKUrIPPlhHgAAzEzAH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzE0AH0AAAAFZAAgAAAAAJaRYmo8zqI2BEUzdSwp4tVRpPmVWsfydkYN3UHh6TMuBXMAIAAAAAAeD6mDnQeLlbC9i0sVgE8+RH6y+e94OJQ0tJ0PvblVSgVsACAAAAAAWp4jvretbDEsqEMzP/WLTnwOiJwCtfrCiB6m8k+yEMoAAzE1AH0AAAAFZAAgAAAAAAZZ538coNPwyRjhEwr5P8Xw32oWOJF+R+nfCGgy2qO3BXMAIAAAAACOPLnJlKwGNPDBReRKnHfteq0wFb3ezhrc7BVXs8RUHwVsACAAAAAA+lGesNk3+SyB/60rSvdQ2aN2vfJPR7llJVhufGTNhHkAAzE2AH0AAAAFZAAgAAAAAFH9l9GGA1I52atJV5jNUf1lx8jBjoEoVoME97v5GFJiBXMAIAAAAAC1qH3Kd78Dr9NGbw7y9D/XYBwv5h1LLO8la5OU7g8UkQVsACAAAAAArZ6atJCYrVfHB8dSNPOFf6nnDADBMJcIEj8ljPvxHp8AAzE3AH0AAAAFZAAgAAAAADtbVEI2tdkrowEMdkacD2w0Y3T3Ofi7PH6HmA6sP0c/BXMAIAAAAADuBSROnZHA+NgUPH8d0LnWFiDsM2bY8bzjC1+elSsIygVsACAAAAAAR0G2m+uANoWknkr/NerFcG+fECVxNIs0cqbY1t/U/0MAAzE4AH0AAAAFZAAgAAAAAAh3WpeMVlikPFYj9hLj+fmIqVt6omCSF75W3TPExyWpBXMAIAAAAAAsQkRmwqeVj2gGE03orb6PtrIzDt6dDU3hgSQi8E2wKgVsACAAAAAA3GHaRE2RAcaBRd8VzmYzWeBD2Gmy91eTK1k8YdWObZcAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: find - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDoublePrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedDoublePrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Update.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Update.yml deleted file mode 100644 index 75a58d545e..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Update.yml +++ /dev/null @@ -1,339 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDouble': '0.0'}, 'max': {'$numberDouble': '200.0'}, 'precision': {'$numberInt': '2'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range DoublePrecision. Update." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } - - name: updateOne - arguments: - filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0" } } } - update: { "$set": { "encryptedDoublePrecision": { $numberDouble: "2" } }} - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command_name: update - command: - - "update": "default" - "ordered": true - "updates": [ - { - "q": { - "encryptedDoublePrecision": { - "$gt": { - "$binary": { - "base64": "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", - "subType": "06" - } - } - } - }, - "u": { - "$set": { - "encryptedDoublePrecision": { $$type: "binData" } - } - } - } - ] - encryptionInformation: - type: 1 - schema: - "default.default": - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - "$db": "default" - - - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedDoublePrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedDoublePrecision": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Aggregate.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Aggregate.yml deleted file mode 100644 index 6b6f83efaf..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Aggregate.yml +++ /dev/null @@ -1,242 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberInt': '0'}, 'max': {'$numberInt': '200'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Int. Aggregate." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } - - name: aggregate - arguments: - pipeline: [{ $match: { "encryptedInt": { $gt: {$numberInt: "0" }} } }] - result: [*doc1] - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - aggregate: *collection_name - pipeline: [ - { - "$match": { - "encryptedInt": { - "$gt": { - "$binary": { - "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - } - } - ] - cursor: {} - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: aggregate - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedInt": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedInt": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Correctness.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Correctness.yml deleted file mode 100644 index 8b27d8fdd2..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Correctness.yml +++ /dev/null @@ -1,424 +0,0 @@ -# Test correctness results. -# Does not include command monitoring expectations or outcome assertions to make tests more readable. - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberInt': '0'}, 'max': {'$numberInt': '200'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "Find with $gt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } - - name: find - arguments: - filter: { encryptedInt: { $gt: { $numberInt: "0" } }} - result: [*doc1] - - - description: "Find with $gte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedInt: { $gte: { $numberInt: "0" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $gt with no results" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedInt: { $gt: { $numberInt: "1" } }} - result: [] - - - description: "Find with $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedInt: { $lt: { $numberInt: "1" } }} - result: [*doc0] - - - description: "Find with $lte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedInt: { $lte: { $numberInt: "1" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $lt below min" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedInt: { $lt: { $numberInt: "0" } }} - result: - errorContains: must be greater than the range minimum - - - description: "Find with $gt above max" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedInt: { $gt: { $numberInt: "200" } }} - result: - errorContains: must be less than the range maximum - - - description: "Find with $gt and $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedInt: { $gt: { $numberInt: "0" }, $lt: { $numberInt: "2"} }} - result: [*doc1] - - - description: "Find with equality" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedInt: { $numberInt: "0" } } - result: [*doc0] - - name: find - arguments: - filter: { encryptedInt: { $numberInt: "1" } } - result: [*doc1] - - - description: "Find with full range" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedInt: { $gte: {$numberInt: "0"}, $lte: {$numberInt: "200"} } } - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $in" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedInt: { $in: [ {$numberInt: "0"} ] } } - result: [*doc0] - - - description: "Insert out of range" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: { _id: 0, encryptedInt: { $numberInt: "-1" }} - result: - errorContains: value must be greater than or equal to the minimum value - - - description: "Insert min and max" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: *doc0 - - name: insertOne - arguments: - document: &doc200 { _id: 200, encryptedInt: { $numberInt: "200" }} - - name: find - arguments: - filter: {} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc200] - - - description: "Aggregate with $gte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedInt: { $gte: { $numberInt: "0" } }} } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - - result: [*doc0, *doc1] - - - description: "Aggregate with $gt with no results" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedInt: { $gt: { $numberInt: "1" } }} } - result: [] - - - description: "Aggregate with $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedInt: { $lt: { $numberInt: "1" } }} } - result: [*doc0] - - - description: "Aggregate with $lte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedInt: { $lte: { $numberInt: "1" } }} } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $lt below min" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedInt: { $lt: { $numberInt: "0" } }} } - result: - errorContains: must be greater than the range minimum - - - description: "Aggregate with $gt above max" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedInt: { $gt: { $numberInt: "200" } }} } - result: - errorContains: must be less than the range maximum - - - description: "Aggregate with $gt and $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedInt: { $gt: { $numberInt: "0" }, $lt: { $numberInt: "2"} }} } - result: [*doc1] - - - description: "Aggregate with equality" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedInt: { $numberInt: "0" } } } - result: [*doc0] - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedInt: { $numberInt: "1" } } } - result: [*doc1] - - - description: "Aggregate with full range" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedInt: { $gte: {$numberInt: "0"}, $lte: {$numberInt: "200"} } } } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $in" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedInt: { $in: [ {$numberInt: "0"} ] } } } - result: [*doc0] - - - description: "Wrong type: Insert Double" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: { _id: 0, encryptedInt: { $numberDouble: "0" }} } - result: - # Expect an error from mongocryptd. - errorContains: "cannot encrypt element" - - - description: "Wrong type: Find Double" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: find - arguments: - filter: { encryptedInt: { $gte: { $numberDouble: "0" } }} - result: - # expect an error from libmongocrypt. - errorContains: "field type is not supported" \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Delete.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Delete.yml deleted file mode 100644 index ff47ff79a0..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Delete.yml +++ /dev/null @@ -1,183 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberInt': '0'}, 'max': {'$numberInt': '200'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Int. Delete." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } - - name: deleteOne - arguments: - filter: { "encryptedInt": { $gt: {$numberInt: "0" }} } - result: - deletedCount: 1 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - delete: *collection_name - deletes: [ - { - "q": { - "encryptedInt": { - "$gt": { - "$binary": { - "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - }, - "limit": 1 - } - ] - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: delete - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedInt": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-FindOneAndUpdate.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-FindOneAndUpdate.yml deleted file mode 100644 index da0e0284af..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-FindOneAndUpdate.yml +++ /dev/null @@ -1,240 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberInt': '0'}, 'max': {'$numberInt': '200'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Int. FindOneAndUpdate." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } - - name: findOneAndUpdate - arguments: - filter: { encryptedInt: { $gt: {$numberInt: "0"}} } - update: { "$set": { "encryptedInt": {$numberInt: "2"}}} - returnDocument: Before - result: *doc1 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - findAndModify: *collection_name - query: { - "encryptedInt": { - "$gt": { - "$binary": { - "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - } - update: { "$set": {"encryptedInt": { $$type: "binData" }} } - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: findAndModify - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedInt": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedInt": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-InsertFind.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-InsertFind.yml deleted file mode 100644 index 4a40a80979..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-InsertFind.yml +++ /dev/null @@ -1,236 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberInt': '0'}, 'max': {'$numberInt': '200'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Int. Insert and Find." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } - - name: find - arguments: - filter: { encryptedInt: { $gt: { $numberInt: "0" } } } - result: [*doc1] - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - find: *collection_name - filter: - "encryptedInt": { - "$gt": { - "$binary": { - "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: find - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedInt": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedInt": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Update.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Update.yml deleted file mode 100644 index 36a2e3124f..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Update.yml +++ /dev/null @@ -1,255 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberInt': '0'}, 'max': {'$numberInt': '200'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Int. Update." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } - - name: updateOne - arguments: - filter: { encryptedInt: { $gt: { $numberInt: "0" } } } - update: { "$set": { "encryptedInt": { $numberInt: "2" } }} - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command_name: update - command: - - "update": "default" - "ordered": true - "updates": [ - { - "q": { - "encryptedInt": { - "$gt": { - "$binary": { - "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - }, - "u": { - "$set": { - "encryptedInt": { $$type: "binData" } - } - } - } - ] - encryptionInformation: - type: 1 - schema: - "default.default": - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - "$db": "default" - - - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedInt": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedInt": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Aggregate.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Aggregate.yml deleted file mode 100644 index 3faf32b509..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Aggregate.yml +++ /dev/null @@ -1,242 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberLong': '0'}, 'max': {'$numberLong': '200'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Long. Aggregate." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } - - name: aggregate - arguments: - pipeline: [{ $match: { "encryptedLong": { $gt: {$numberLong: "0" }} } }] - result: [*doc1] - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - aggregate: *collection_name - pipeline: [ - { - "$match": { - "encryptedLong": { - "$gt": { - "$binary": { - "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - } - } - ] - cursor: {} - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: aggregate - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedLong": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedLong": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Correctness.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Correctness.yml deleted file mode 100644 index f43c6b4c82..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Correctness.yml +++ /dev/null @@ -1,423 +0,0 @@ -# Test correctness results. -# Does not include command monitoring expectations or outcome assertions to make tests more readable. - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberLong': '0'}, 'max': {'$numberLong': '200'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "Find with $gt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } - - name: find - arguments: - filter: { encryptedLong: { $gt: { $numberLong: "0" } }} - result: [*doc1] - - - description: "Find with $gte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedLong: { $gte: { $numberLong: "0" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $gt with no results" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedLong: { $gt: { $numberLong: "1" } }} - result: [] - - - description: "Find with $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedLong: { $lt: { $numberLong: "1" } }} - result: [*doc0] - - - description: "Find with $lte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedLong: { $lte: { $numberLong: "1" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $lt below min" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedLong: { $lt: { $numberLong: "0" } }} - result: - errorContains: must be greater than the range minimum - - - description: "Find with $gt above max" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedLong: { $gt: { $numberLong: "200" } }} - result: - errorContains: must be less than the range maximum - - - description: "Find with $gt and $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedLong: { $gt: { $numberLong: "0" }, $lt: { $numberLong: "2"} }} - result: [*doc1] - - - description: "Find with equality" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedLong: { $numberLong: "0" } } - result: [*doc0] - - name: find - arguments: - filter: { encryptedLong: { $numberLong: "1" } } - result: [*doc1] - - - description: "Find with full range" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedLong: { $gte: {$numberLong: "0"}, $lte: {$numberLong: "200"} } } - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc1] - - - description: "Find with $in" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: find - arguments: - filter: { encryptedLong: { $in: [ {$numberLong: "0"} ] } } - result: [*doc0] - - - description: "Insert out of range" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: { _id: 0, encryptedLong: { $numberLong: "-1" }} - result: - errorContains: value must be greater than or equal to the minimum value - - - description: "Insert min and max" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: *doc0 - - name: insertOne - arguments: - document: &doc200 { _id: 200, encryptedLong: { $numberLong: "200" }} - - name: find - arguments: - filter: {} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: [*doc0, *doc200] - - - description: "Aggregate with $gte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedLong: { $gte: { $numberLong: "0" } }} } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $gt with no results" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedLong: { $gt: { $numberLong: "1" } }} } - result: [] - - - description: "Aggregate with $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedLong: { $lt: { $numberLong: "1" } }} } - result: [*doc0] - - - description: "Aggregate with $lte" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedLong: { $lte: { $numberLong: "1" } }} } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $lt below min" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedLong: { $lt: { $numberLong: "0" } }} } - result: - errorContains: must be greater than the range minimum - - - description: "Aggregate with $gt above max" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedLong: { $gt: { $numberLong: "200" } }} } - result: - errorContains: must be less than the range maximum - - - description: "Aggregate with $gt and $lt" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedLong: { $gt: { $numberLong: "0" }, $lt: { $numberLong: "2"} }} } - result: [*doc1] - - - description: "Aggregate with equality" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedLong: { $numberLong: "0" } } } - result: [*doc0] - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedLong: { $numberLong: "1" } } } - result: [*doc1] - - - description: "Aggregate with full range" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedLong: { $gte: {$numberLong: "0"}, $lte: {$numberLong: "200"} } } } - # sort so results from range queries are ordered. - - { $sort: { _id: 1 }} - result: [*doc0, *doc1] - - - description: "Aggregate with $in" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: *doc0 } - - name: insertOne - arguments: { document: *doc1 } - - name: aggregate - arguments: - pipeline: - - { $match: { encryptedLong: { $in: [ {$numberLong: "0"} ] } } } - result: [*doc0] - - - description: "Wrong type: Insert Double" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: { _id: 0, encryptedLong: { $numberDouble: "0" }} } - result: - # Expect an error from mongocryptd. - errorContains: "cannot encrypt element" - - - description: "Wrong type: Find Double" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: find - arguments: - filter: { encryptedLong: { $gte: { $numberDouble: "0" } }} - result: - # expect an error from libmongocrypt. - errorContains: "field type is not supported" \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Delete.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Delete.yml deleted file mode 100644 index a5a8fe9921..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Delete.yml +++ /dev/null @@ -1,183 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberLong': '0'}, 'max': {'$numberLong': '200'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Long. Delete." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } - - name: deleteOne - arguments: - filter: { "encryptedLong": { $gt: {$numberLong: "0" }} } - result: - deletedCount: 1 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - delete: *collection_name - deletes: [ - { - "q": { - "encryptedLong": { - "$gt": { - "$binary": { - "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - }, - "limit": 1 - } - ] - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: delete - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedLong": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-FindOneAndUpdate.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-FindOneAndUpdate.yml deleted file mode 100644 index f6312575fc..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-FindOneAndUpdate.yml +++ /dev/null @@ -1,240 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberLong': '0'}, 'max': {'$numberLong': '200'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Long. FindOneAndUpdate." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } - - name: findOneAndUpdate - arguments: - filter: { encryptedLong: { $gt: {$numberLong: "0"}} } - update: { "$set": { "encryptedLong": {$numberLong: "2"}}} - returnDocument: Before - result: *doc1 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - findAndModify: *collection_name - query: { - "encryptedLong": { - "$gt": { - "$binary": { - "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - } - update: { "$set": {"encryptedLong": { $$type: "binData" }} } - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: findAndModify - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedLong": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedLong": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-InsertFind.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-InsertFind.yml deleted file mode 100644 index 11ffe382af..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-InsertFind.yml +++ /dev/null @@ -1,236 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberLong': '0'}, 'max': {'$numberLong': '200'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Long. Insert and Find." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } - - name: find - arguments: - filter: { encryptedLong: { $gt: { $numberLong: "0" } } } - result: [*doc1] - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - find: *collection_name - filter: - "encryptedLong": { - "$gt": { - "$binary": { - "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: find - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedLong": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedLong": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Update.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Update.yml deleted file mode 100644 index f69a54d332..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Update.yml +++ /dev/null @@ -1,255 +0,0 @@ - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberLong': '0'}, 'max': {'$numberLong': '200'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "FLE2 Range Long. Update." - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } - - name: insertOne - arguments: - document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } - - name: updateOne - arguments: - filter: { encryptedLong: { $gt: { $numberLong: "0" } } } - update: { "$set": { "encryptedLong": { $numberLong: "2" } }} - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - expectations: - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - - command_started_event: - command: - find: datakeys - filter: { - "$or": [ - { - "_id": { - "$in": [ - {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - } - $db: keyvault - readConcern: { level: "majority" } - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command: - insert: *collection_name - documents: - - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } - ordered: true - encryptionInformation: - type: 1 - schema: - default.default: - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - command_name: insert - - command_started_event: - command_name: update - command: - - "update": "default" - "ordered": true - "updates": [ - { - "q": { - "encryptedLong": { - "$gt": { - "$binary": { - "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", - "subType": "06" - } - } - } - }, - "u": { - "$set": { - "encryptedLong": { $$type: "binData" } - } - } - } - ] - encryptionInformation: - type: 1 - schema: - "default.default": - # libmongocrypt applies escCollection and ecocCollection to outgoing command. - escCollection: "enxcol_.default.esc" - ecocCollection: "enxcol_.default.ecoc" - <<: *encrypted_fields - "$db": "default" - - - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - - { - "_id": 0, - "encryptedLong": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", - "subType": "00" - } - } - ] - } - - - { - "_id": 1, - "encryptedLong": { $$type: "binData" }, - "__safeContent__": [ - { - "$binary": { - "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", - "subType": "00" - } - }, - { - "$binary": { - "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", - "subType": "00" - } - } - ] - } \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-WrongType.yml b/spec/spec_tests/data/client_side_encryption/fle2v2-Range-WrongType.yml deleted file mode 100644 index 2676b5591b..0000000000 --- a/spec/spec_tests/data/client_side_encryption/fle2v2-Range-WrongType.yml +++ /dev/null @@ -1,44 +0,0 @@ -# Test correctness results. -# Does not include command monitoring expectations or outcome assertions to make tests more readable. - -# Requires libmongocrypt 1.8.0. -runOn: - - minServerVersion: "7.0.0" - maxServerVersion: "7.99.99" - # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. - # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded", "load-balanced" ] -database_name: &database_name "default" -collection_name: &collection_name "default" -data: [] -encrypted_fields: &encrypted_fields { 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberInt': '0'}, 'max': {'$numberInt': '200'}}}]} -key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] -tests: - - description: "Wrong type: Insert Double" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: { _id: 0, encryptedInt: { $numberDouble: "0" }} } - result: - # Expect an error from mongocryptd. - errorContains: "cannot encrypt element" - - - description: "Wrong type: Find Double" - clientOptions: - autoEncryptOpts: - kmsProviders: - local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} - operations: - - name: insertOne - arguments: { document: { _id: 0, encryptedInt: { $numberInt: "0" }} } - - name: find - arguments: - filter: { encryptedInt: { $gte: { $numberDouble: "0" } }} - # sort so results from range queries are ordered. - sort: { _id: 1 } - result: - # expect an error from libmongocrypt. - errorContains: "field type is not supported" diff --git a/spec/spec_tests/data/client_side_encryption/timeoutMS.yml b/spec/spec_tests/data/client_side_encryption/timeoutMS.yml new file mode 100644 index 0000000000..7f487fcf94 --- /dev/null +++ b/spec/spec_tests/data/client_side_encryption/timeoutMS.yml @@ -0,0 +1,67 @@ +runOn: + - minServerVersion: "4.4" +database_name: &database_name "cse-timeouts-db" +collection_name: &collection_name "cse-timeouts-coll" + +data: [] +json_schema: {'properties': {'encrypted_w_altname': {'encrypt': {'keyId': '/altname', 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Random'}}, 'encrypted_string': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'random': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Random'}}, 'encrypted_string_equivalent': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} +key_vault_data: [{'status': 1, '_id': {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}, 'masterKey': {'provider': 'aws', 'key': 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0', 'region': 'us-east-1'}, 'updateDate': {'$date': {'$numberLong': '1552949630483'}}, 'keyMaterial': {'$binary': {'base64': 'AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1552949630483'}}, 'keyAltNames': ['altname', 'another_altname']}] + +tests: + - description: "timeoutMS applied to listCollections to get collection schema" + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 60 + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + timeoutMS: 50 + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0", random: "abc" } + result: + isTimeoutError: true + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + maxTimeMS: { $$type: ["int", "long"] } + command_name: listCollections + + # Test that timeoutMS applies to the sum of all operations done for client-side encryption. This is done by blocking + # listCollections and find for 30ms each and running an insertOne with timeoutMS=50. There should be one + # listCollections command and one "find" command, so the sum should take more than timeoutMS. A second listCollections + # event doesn't occur due to the internal MongoClient lacking configured auto encryption, plus libmongocrypt holds the + # collection schema in cache for a minute. + # + # This test does not include command monitoring expectations because the exact command sequence is dependent on the + # amount of time taken by mongocryptd communication. In slow runs, mongocryptd communication can breach the timeout + # and result in the final "find" not being sent. + - description: "remaining timeoutMS applied to find to get keyvault data" + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listCollections", "find"] + blockConnection: true + blockTimeMS: 30 + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + timeoutMS: 50 + operations: + - name: insertOne + arguments: + document: *doc0 + result: + isTimeoutError: true diff --git a/spec/spec_tests/data/client_side_operations_timeout/bulkWrite.yml b/spec/spec_tests/data/client_side_operations_timeout/bulkWrite.yml new file mode 100644 index 0000000000..0459dbbc16 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/bulkWrite.yml @@ -0,0 +1,87 @@ +description: "timeoutMS behaves correctly for bulkWrite operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + uriOptions: + # Used to speed up the test + w: 1 + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # Test that drivers do not refresh timeoutMS between commands. This is done by running a bulkWrite that will require + # two commands with timeoutMS=200 and blocking each command for 120ms. The server should take over 200ms total, so the + # bulkWrite should fail with a timeout error. + - description: "timeoutMS applied to entire bulkWrite, not individual commands" + operations: + # Do an operation without a timeout to ensure the servers are discovered. + - name: insertOne + object: *collection + arguments: + document: {} + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert", "update"] + blockConnection: true + blockTimeMS: 120 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + - replaceOne: + filter: { _id: 1 } + replacement: { x: 1 } + timeoutMS: 200 + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } diff --git a/spec/spec_tests/data/client_side_operations_timeout/change-streams.yml b/spec/spec_tests/data/client_side_operations_timeout/change-streams.yml new file mode 100644 index 0000000000..683c30674f --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/change-streams.yml @@ -0,0 +1,358 @@ +description: "timeoutMS behaves correctly for change streams" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + # Drivers are not required to execute killCursors during resume attempts, so it should be ignored for command + # monitoring assertions. + ignoreCommandMonitoringEvents: ["killCursors"] + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - description: "error if maxAwaitTimeMS is greater than timeoutMS" + operations: + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + timeoutMS: 5 + maxAwaitTimeMS: 10 + expectError: + isClientError: true + + - description: "error if maxAwaitTimeMS is equal to timeoutMS" + operations: + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + timeoutMS: 5 + maxAwaitTimeMS: 5 + expectError: + isClientError: true + + - description: "timeoutMS applied to initial aggregate" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 55 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + timeoutMS: 50 + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + + # If maxAwaitTimeMS is not set, timeoutMS should be refreshed for the getMore and the getMore should not have a + # maxTimeMS field. This test requires a high timeout because the server applies a default 1000ms maxAwaitTime. To + # ensure that the driver is refreshing the timeout between commands, the test blocks aggregate and getMore commands + # for 30ms each and creates/iterates a change stream with timeoutMS=1050. The initial aggregate will block for 30ms + # and the getMore will block for 1030ms. + - description: "timeoutMS is refreshed for getMore if maxAwaitTimeMS is not set" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate", "getMore"] + blockConnection: true + blockTimeMS: 30 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + timeoutMS: 1050 + saveResultAsEntity: &changeStream changeStream + - name: iterateOnce + object: *changeStream + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + + # If maxAwaitTimeMS is set, timeoutMS should still be refreshed for the getMore and the getMore command should have a + # maxTimeMS field. + - description: "timeoutMS is refreshed for getMore if maxAwaitTimeMS is set" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate", "getMore"] + blockConnection: true + # was 15, changed to 30 to account for jruby driver latency. + blockTimeMS: 30 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + # was 20, changed to 29 to account for native ruby driver latency. + # Changed again to 59 to account for additional jruby driver latency. + # The idea for this test is that each operation is delayed by 15ms + # (by failpoint). the timeout for each operation is set to (originally) + # 20ms, because if timeoutMS was not refreshed for getMore, it would timeout. + # However, we're tickling the 20ms timeout because the driver itself + # is taking more than 5ms to do its thing. + # + # Changing the blockTimeMS in the failpoint to 30ms, and then bumping + # the timeout to almost twice that (59ms) should give us the same + # effect in the test. + timeoutMS: 59 + batchSize: 2 + maxAwaitTimeMS: 1 + saveResultAsEntity: &changeStream changeStream + - name: iterateOnce + object: *changeStream + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: 1 + + # The timeout should be applied to the entire resume attempt, not individually to each command. The test creates a + # change stream with timeoutMS=20 which returns an empty initial batch and then sets a fail point to block both + # getMore and aggregate for 12ms each and fail with a resumable error. When the resume attempt happens, the getMore + # and aggregate block for longer than 20ms total, so it times out. + - description: "timeoutMS applies to full resume attempt in a next call" + operations: + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + # Originally set to 20, but the Ruby driver was too-often taking + # that much time, and causing the timing of the test to fail. Instead, + # bumped the timout to 23ms, which is just less than twice the + # blockTimeMS for the failpoint. It still failed on jruby, so the + # timeout (and blockTimeMS) were drastically increased to accomodate + # JRuby. This tests the same thing, but gives the driver a bit more + # breathing space. + timeoutMS: 99 + saveResultAsEntity: &changeStream changeStream + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["getMore", "aggregate"] + blockConnection: true + # Originally 12, bumped it to 50 to give the jruby driver a bit + # more breathing room. + blockTimeMS: 50 + errorCode: 7 # HostNotFound - resumable but does not require an SDAM state change. + # failCommand doesn't correctly add the ResumableChangeStreamError by default. It needs to be specified + # manually here so the error is considered resumable. The failGetMoreAfterCursorCheckout fail point + # would add the label without this, but it does not support blockConnection functionality. + errorLabels: ["ResumableChangeStreamError"] + - name: iterateUntilDocumentOrError + object: *changeStream + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + + - description: "change stream can be iterated again if previous iteration times out" + operations: + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + # Specify a short maxAwaitTimeMS because otherwise the getMore on the new cursor will wait for 1000ms and + # time out. + maxAwaitTimeMS: 1 + timeoutMS: 100 + saveResultAsEntity: &changeStream changeStream + # Block getMore for 150ms to force the next() call to time out. + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + blockTimeMS: 150 + # The original aggregate didn't return any events so this should do a getMore and return a timeout error. + - name: iterateUntilDocumentOrError + object: *changeStream + expectError: + isTimeoutError: true + # The previous iteration attempt timed out so this should re-create the change stream. We use iterateOnce rather + # than iterateUntilDocumentOrError because there haven't been any events and we only want to assert that the + # cursor was re-created. + - name: iterateOnce + object: *changeStream + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + # The iterateUntilDocumentOrError operation should send a getMore. + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + # The iterateOnce operation should re-create the cursor via an aggregate and then send a getMore to iterate + # the new cursor. + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + + # The timeoutMS value should be refreshed for getMore's. This is a failure test. The createChangeStream operation + # sets timeoutMS=10 and the getMore blocks for 15ms, causing iteration to fail with a timeout error. + - description: "timeoutMS is refreshed for getMore - failure" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + # blockTimeMS: 15 + # Increase timeout + blockTimeMS: 30 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + # timeoutMS: 10 + # Increase timeout + timeoutMS: 20 + saveResultAsEntity: &changeStream changeStream + # The first iteration should do a getMore + - name: iterateUntilDocumentOrError + object: *changeStream + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + # The iterateUntilDocumentOrError operation should send a getMore. + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName diff --git a/spec/spec_tests/data/client_side_operations_timeout/close-cursors.yml b/spec/spec_tests/data/client_side_operations_timeout/close-cursors.yml new file mode 100644 index 0000000000..352f602cbb --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/close-cursors.yml @@ -0,0 +1,129 @@ +description: "timeoutMS behaves correctly when closing cursors" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 0 } + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "timeoutMS is refreshed for close" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + blockTimeMS: 50 + - name: createFindCursor + object: *collection + arguments: + filter: {} + batchSize: 2 + timeoutMS: 20 + saveResultAsEntity: &cursor cursor + # Iterate the cursor three times. The third should do a getMore, which should fail with a timeout error. + - name: iterateUntilDocumentOrError + object: *cursor + - name: iterateUntilDocumentOrError + object: *cursor + - name: iterateUntilDocumentOrError + object: *cursor + expectError: + isTimeoutError: true + # All errors from close() are ignored, so we close the cursor here but assert that killCursors was executed + # successfully via command monitoring expectations below. + - name: close + object: *cursor + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + - commandStartedEvent: + commandName: getMore + - commandFailedEvent: + commandName: getMore + - commandStartedEvent: + command: + killCursors: *collectionName + # The close() operation should inherit timeoutMS from the initial find(). + maxTimeMS: { $$type: ["int", "long"] } + commandName: killCursors + - commandSucceededEvent: + commandName: killCursors + + - description: "timeoutMS can be overridden for close" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["killCursors"] + blockConnection: true + blockTimeMS: 30 + - name: createFindCursor + object: *collection + arguments: + filter: {} + batchSize: 2 + timeoutMS: 20 + saveResultAsEntity: &cursor cursor + - name: close + object: *cursor + arguments: + # timeoutMS: 40 + # Increase timeout + timeoutMS: 50 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + - commandStartedEvent: + command: + killCursors: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + commandName: killCursors + - commandSucceededEvent: + commandName: killCursors diff --git a/spec/spec_tests/data/client_side_operations_timeout/command-execution.yml b/spec/spec_tests/data/client_side_operations_timeout/command-execution.yml new file mode 100644 index 0000000000..400a90867a --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/command-execution.yml @@ -0,0 +1,250 @@ +description: "timeoutMS behaves correctly during command execution" + +schemaVersion: "1.9" + +runOnRequirements: + # The appName filter cannot be used to set a fail point on connection handshakes until server version 4.9 due to + # SERVER-49220/SERVER-49336. + - minServerVersion: "4.9" + # Skip load-balanced and serverless which do not support RTT measurements. + topologies: [ single, replicaset, sharded ] + serverless: forbid + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + +initialData: + # The corresponding entities for the collections defined here are created in test-level createEntities operations. + # This is done so that tests can set fail points that will affect all of the handshakes and heartbeats done by a + # client. The collection and database names are listed here so that the collections will be dropped and re-created at + # the beginning of each test. + - collectionName: ®ularCollectionName coll + databaseName: &databaseName test + documents: [] + - collectionName: &timeoutCollectionName timeoutColl + databaseName: &databaseName test + documents: [] + +tests: + - description: "maxTimeMS value in the command is less than timeoutMS" + operations: + # Artificially increase the server RTT to ~50ms. + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: "alwaysOn" + data: + failCommands: ["hello", "isMaster"] + appName: &appName reduceMaxTimeMSTest + blockConnection: true + blockTimeMS: 50 + # Create a client with the app name specified in the fail point and timeoutMS higher than blockTimeMS. + # Also create database and collection entities derived from the new client. + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + uriOptions: + appName: *appName + w: 1 # Override server's w:majority default to speed up the test. + timeoutMS: 500 + heartbeatFrequencyMS: 500 + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &timeoutCollection timeoutCollection + database: *database + collectionName: *timeoutCollectionName + # Do an operation with a large timeout to ensure the servers are discovered. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 1 } + timeoutMS: 100000 + # Wait until short-circuiting has been enabled (at least 2 RTT measurements). + - name: wait + object: testRunner + arguments: + ms: 1000 + # Do an operation with timeoutCollection so the event will include a maxTimeMS field. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 2 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *timeoutCollectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *timeoutCollectionName + maxTimeMS: { $$lte: 450 } + + - description: "command is not sent if RTT is greater than timeoutMS" + operations: + # Artificially increase the server RTT to ~50ms. + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: "alwaysOn" + data: + failCommands: ["hello", "isMaster"] + appName: &appName rttTooHighTest + blockConnection: true + blockTimeMS: 50 + # Create a client with the app name specified in the fail point. Also create database and collection entities + # derived from the new client. There is one collection entity with no timeoutMS and another with a timeoutMS + # that's lower than the fail point's blockTimeMS value. + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + uriOptions: + appName: *appName + w: 1 # Override server's w:majority default to speed up the test. + timeoutMS: 10 + heartbeatFrequencyMS: 500 + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &timeoutCollection timeoutCollection + database: *database + collectionName: *timeoutCollectionName + # Do an operation with a large timeout to ensure the servers are discovered. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 1 } + timeoutMS: 100000 + # Wait until short-circuiting has been enabled (at least 2 RTT measurements). + - name: wait + object: testRunner + arguments: + ms: 1000 + # Do an operation with timeoutCollection which will error. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 2 } + expectError: + isTimeoutError: true + # Do an operation with timeoutCollection which will error. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 3 } + expectError: + isTimeoutError: true + # Do an operation with timeoutCollection which will error. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 4 } + expectError: + isTimeoutError: true + expectEvents: + # There should only be one event, which corresponds to the first + # insertOne call. For the subsequent insertOne calls, drivers should + # fail client-side. + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *timeoutCollectionName + + - description: "short-circuit is not enabled with only 1 RTT measurement" + operations: + # Artificially increase the server RTT to ~300ms. + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: "alwaysOn" + data: + failCommands: ["hello", "isMaster"] + appName: &appName reduceMaxTimeMSTest + blockConnection: true + blockTimeMS: 100 + # Create a client with the app name specified in the fail point and timeoutMS lower than blockTimeMS. + # Also create database and collection entities derived from the new client. + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + uriOptions: + appName: *appName + w: 1 # Override server's w:majority default to speed up the test. + timeoutMS: 90 + heartbeatFrequencyMS: 100000 # Override heartbeatFrequencyMS to ensure only 1 RTT is recorded. + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &timeoutCollection timeoutCollection + database: *database + collectionName: *timeoutCollectionName + # Do an operation with a large timeout to ensure the servers are discovered. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 1 } + timeoutMS: 100000 + # Do an operation with timeoutCollection which will succeed. If this + # fails it indicates the driver mistakenly used the min RTT even though + # there has only been one sample. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 2 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *timeoutCollectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *timeoutCollectionName + maxTimeMS: { $$lte: 450 } diff --git a/spec/spec_tests/data/client_side_operations_timeout/convenient-transactions.yml b/spec/spec_tests/data/client_side_operations_timeout/convenient-transactions.yml new file mode 100644 index 0000000000..050d0d514f --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/convenient-transactions.yml @@ -0,0 +1,113 @@ +description: "timeoutMS behaves correctly for the withTransaction API" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 50 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + - session: + id: &session session + client: *client + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - description: "withTransaction raises a client-side error if timeoutMS is overridden inside the callback" + operations: + - name: withTransaction + object: *session + arguments: + callback: + - name: insertOne + object: *collection + arguments: + document: { _id: 1 } + session: *session + timeoutMS: 100 + expectError: + isClientError: true + expectEvents: + # The only operation run fails with a client-side error, so there should be no events for the client. + - client: *client + events: [] + + - description: "timeoutMS is not refreshed for each operation in the callback" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + blockConnection: true + # Was 30, but JRuby was taking too long in preparing and issuing + # the operation. We now specify the timeoutMS below, and set this + # value to just more than half of it (so that two inserts will + # exceed the timeout, but one won't--or shouldn't). + blockTimeMS: 51 + - name: withTransaction + object: *session + arguments: + # Was originally not specified here, inheriting the client value of 50ms. + # That wasn't giving JRuby enough time, so we specify a larger value + # here. + timeoutMS: 100 + callback: + - name: insertOne + object: *collection + arguments: + document: { _id: 1 } + session: *session + - name: insertOne + object: *collection + arguments: + document: { _id: 2 } + session: *session + expectError: + isTimeoutError: true + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + # Because the second insert expects an error and gets an error, it technically succeeds, so withTransaction + # will try to run commitTransaction. This will fail client-side, though, because the timeout has already + # expired, so no command is sent. + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } diff --git a/spec/spec_tests/data/client_side_operations_timeout/cursors.yml b/spec/spec_tests/data/client_side_operations_timeout/cursors.yml new file mode 100644 index 0000000000..0202054732 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/cursors.yml @@ -0,0 +1,70 @@ +description: "tests for timeoutMS behavior that applies to all cursor types" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client client + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - description: "find errors if timeoutMode is set and timeoutMS is not" + operations: + - name: find + object: *collection + arguments: + filter: {} + timeoutMode: cursorLifetime + expectError: + isClientError: true + + - description: "collection aggregate errors if timeoutMode is set and timeoutMS is not" + operations: + - name: aggregate + object: *collection + arguments: + pipeline: [] + timeoutMode: cursorLifetime + expectError: + isClientError: true + + - description: "database aggregate errors if timeoutMode is set and timeoutMS is not" + operations: + - name: aggregate + object: *database + arguments: + pipeline: [] + timeoutMode: cursorLifetime + expectError: + isClientError: true + + - description: "listCollections errors if timeoutMode is set and timeoutMS is not" + operations: + - name: listCollections + object: *database + arguments: + filter: {} + timeoutMode: cursorLifetime + expectError: + isClientError: true + + - description: "listIndexes errors if timeoutMode is set and timeoutMS is not" + operations: + - name: listIndexes + object: *collection + arguments: + timeoutMode: cursorLifetime + expectError: + isClientError: true diff --git a/spec/spec_tests/data/client_side_operations_timeout/deprecated-options.yml b/spec/spec_tests/data/client_side_operations_timeout/deprecated-options.yml new file mode 100644 index 0000000000..31eeb8d089 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/deprecated-options.yml @@ -0,0 +1,3982 @@ +description: "operations ignore deprecated timeout options if timeoutMS is set" + +schemaVersion: "1.9" + +# Most tests in this file can be executed against any server version, but some tests execute operations that are only +# available on higher server versions (e.g. abortTransaction). To avoid too many special cases in templated tests, the +# min server version is set to 4.2 for all. +runOnRequirements: + - minServerVersion: "4.2" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + +initialData: + - collectionName: &collectionName coll + databaseName: &databaseName test + documents: [] + +tests: + # For each operation, run these tests: + # + # 1. socketTimeoutMS is ignored if timeoutMS is set. The test creates a client with socketTimeoutMS=1, configures and + # a failpoint to block the operation for 5ms, runs the operation with timeoutMS=10000, and expects it to succeed. + # + # 2. wTimeoutMS is ignored if timeoutMS is set. The test creates a client with wTimeoutMS=1, runs the operation with + # timeoutMS=10000, expects the operation to succeed, and uses command monitoring expectations to assert that the + # command sent to the server does not contain a writeConcern field. + # + # 3. If the operation supports maxTimeMS, it ignores maxTimeMS if timeoutMS is set. The test executes the operation + # with timeoutMS=1000 and maxTimeMS=5000. It expects the operation to succeed and uses command monitoring expectations + # to assert that the actual maxTimeMS value sent was less than or equal to 100, thereby asserting that it was + # actually derived from timeoutMS. + + # Tests for commitTransaction. These are not included in the operations loop because the tests need to execute + # additional "startTransaction" and "insertOne" operations to establish a server-side transaction. There is also one + # additional test to assert that maxCommitTimeMS is ignored if timeoutMS is set. + + - description: "commitTransaction ignores socketTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + # This test uses 20 instead of 1 like other tests because socketTimeoutMS also applies to the + # operation done to start the server-side transaction and it needs time to succeed. + socketTimeoutMS: 20 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: ["aggregate"] + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["commitTransaction"] + blockConnection: true + blockTimeMS: 5 + - name: startTransaction + object: *session + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + - name: commitTransaction + object: *session + arguments: + timeoutMS: 10000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "commitTransaction ignores wTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: ["aggregate"] + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: startTransaction + object: *session + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + - name: commitTransaction + object: *session + arguments: + timeoutMS: 10000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "commitTransaction ignores maxCommitTimeMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: ["aggregate"] + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + sessionOptions: + defaultTransactionOptions: + maxCommitTimeMS: 5000 + - name: startTransaction + object: *session + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + - name: commitTransaction + object: *session + arguments: + timeoutMS: &timeoutMS 1000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + # Assert that the final maxTimeMS field is derived from timeoutMS, not maxCommitTimeMS. + maxTimeMS: { $$lte: *timeoutMS } + + # Tests for abortTransaction. These are not included in the operations loop because the tests need to execute + # additional "startTransaction" and "insertOne" operations to establish a server-side transaction. + + - description: "abortTransaction ignores socketTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + # This test uses 20 instead of 1 like other tests because socketTimeoutMS also applies to the + # operation done to start the server-side transaction and it needs time to succeed. + socketTimeoutMS: 20 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: ["aggregate"] + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["abortTransaction"] + blockConnection: true + blockTimeMS: 5 + - name: startTransaction + object: *session + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + - name: abortTransaction + object: *session + arguments: + timeoutMS: 10000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "abortTransaction ignores wTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: ["aggregate"] + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: startTransaction + object: *session + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + - name: abortTransaction + object: *session + arguments: + timeoutMS: 10000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + # Tests for withTransaction. These are not included in the operations loop because the command monitoring + # expectations contain multiple commands. There is also one additional test to assert that maxCommitTimeMS is ignored + # if timeoutMS is set. + + - description: "withTransaction ignores socketTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + # This test uses 20 instead of 1 like other tests because socketTimeoutMS also applies to the + # operation done to start the server-side transaction and it needs time to succeed. + socketTimeoutMS: 20 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["commitTransaction"] + blockConnection: true + blockTimeMS: 5 + - name: withTransaction + object: *session + arguments: + timeoutMS: 10000 + callback: + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "withTransaction ignores wTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: withTransaction + object: *session + arguments: + timeoutMS: 10000 + callback: + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "withTransaction ignores maxCommitTimeMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + sessionOptions: + defaultTransactionOptions: + maxCommitTimeMS: 5000 + - name: withTransaction + object: *session + arguments: + timeoutMS: &timeoutMS 1000 + callback: + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + # Assert that the final maxTimeMS field is derived from timeoutMS, not maxCommitTimeMS. + maxTimeMS: { $$lte: *timeoutMS } + + # Tests for operations that can be generated. + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - listDatabases on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 5 + - name: listDatabases + object: *client + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - listDatabases on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: listDatabases + object: *client + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - listDatabaseNames on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 5 + - name: listDatabaseNames + object: *client + arguments: + timeoutMS: 100000 + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - listDatabaseNames on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: listDatabaseNames + object: *client + arguments: + timeoutMS: 100000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - createChangeStream on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 5 + - name: createChangeStream + object: *client + arguments: + timeoutMS: 100000 + pipeline: [] + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - createChangeStream on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: createChangeStream + object: *client + arguments: + timeoutMS: 100000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - aggregate on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 5 + - name: aggregate + object: *database + arguments: + timeoutMS: 100000 + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - aggregate on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: aggregate + object: *database + arguments: + timeoutMS: 100000 + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - aggregate on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: aggregate + object: *database + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - listCollections on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 5 + - name: listCollections + object: *database + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - listCollections on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: listCollections + object: *database + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - listCollectionNames on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 5 + - name: listCollectionNames + object: *database + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - listCollectionNames on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: listCollectionNames + object: *database + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - runCommand on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["ping"] + blockConnection: true + blockTimeMS: 5 + - name: runCommand + object: *database + arguments: + timeoutMS: 100000 + command: { ping: 1 } + commandName: ping + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - runCommand on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: runCommand + object: *database + arguments: + timeoutMS: 100000 + command: { ping: 1 } + commandName: ping + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: ping + databaseName: *databaseName + command: + ping: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - createChangeStream on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 5 + - name: createChangeStream + object: *database + arguments: + timeoutMS: 100000 + pipeline: [] + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - createChangeStream on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: createChangeStream + object: *database + arguments: + timeoutMS: 100000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 5 + - name: aggregate + object: *collection + arguments: + timeoutMS: 100000 + pipeline: [] + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: aggregate + object: *collection + arguments: + timeoutMS: 100000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: aggregate + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 5 + - name: count + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: count + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: count + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - countDocuments on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 5 + - name: countDocuments + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - countDocuments on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: countDocuments + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 5 + - name: estimatedDocumentCount + object: *collection + arguments: + timeoutMS: 100000 + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: estimatedDocumentCount + object: *collection + arguments: + timeoutMS: 100000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: estimatedDocumentCount + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 5 + - name: distinct + object: *collection + arguments: + timeoutMS: 100000 + fieldName: x + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: distinct + object: *collection + arguments: + timeoutMS: 100000 + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: distinct + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 5 + - name: find + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: find + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: find + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 5 + - name: findOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOne + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - listIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 5 + - name: listIndexes + object: *collection + arguments: + timeoutMS: 100000 + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - listIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: listIndexes + object: *collection + arguments: + timeoutMS: 100000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - listIndexNames on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 5 + - name: listIndexNames + object: *collection + arguments: + timeoutMS: 100000 + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - listIndexNames on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: listIndexNames + object: *collection + arguments: + timeoutMS: 100000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - createChangeStream on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 5 + - name: createChangeStream + object: *collection + arguments: + timeoutMS: 100000 + pipeline: [] + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - createChangeStream on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: createChangeStream + object: *collection + arguments: + timeoutMS: 100000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - insertOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 5 + - name: insertOne + object: *collection + arguments: + timeoutMS: 100000 + document: { x: 1 } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - insertOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: insertOne + object: *collection + arguments: + timeoutMS: 100000 + document: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - insertMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 5 + - name: insertMany + object: *collection + arguments: + timeoutMS: 100000 + documents: + - { x: 1 } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - insertMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: insertMany + object: *collection + arguments: + timeoutMS: 100000 + documents: + - { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - deleteOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 5 + - name: deleteOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - deleteOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: deleteOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - deleteMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 5 + - name: deleteMany + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - deleteMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: deleteMany + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - replaceOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 5 + - name: replaceOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + replacement: { x: 1 } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - replaceOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: replaceOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - updateOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 5 + - name: updateOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + update: { $set: { x: 1 } } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - updateOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: updateOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - updateMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 5 + - name: updateMany + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + update: { $set: { x: 1 } } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - updateMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: updateMany + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 5 + - name: findOneAndDelete + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOneAndDelete + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOneAndDelete + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 5 + - name: findOneAndReplace + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + replacement: { x: 1 } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOneAndReplace + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOneAndReplace + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 5 + - name: findOneAndUpdate + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + update: { $set: { x: 1 } } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOneAndUpdate + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOneAndUpdate + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - bulkWrite on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 5 + - name: bulkWrite + object: *collection + arguments: + timeoutMS: 100000 + requests: + - insertOne: + document: { _id: 1 } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - bulkWrite on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: bulkWrite + object: *collection + arguments: + timeoutMS: 100000 + requests: + - insertOne: + document: { _id: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 5 + - name: createIndex + object: *collection + arguments: + timeoutMS: 100000 + keys: { x: 1 } + name: "x_1" + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: createIndex + object: *collection + arguments: + timeoutMS: 100000 + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: createIndex + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 5 + - name: dropIndex + object: *collection + arguments: + timeoutMS: 100000 + name: "x_1" + + expectError: + isClientError: false + isTimeoutError: false + + - description: "wTimeoutMS is ignored if timeoutMS is set - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: dropIndex + object: *collection + arguments: + timeoutMS: 100000 + name: "x_1" + + expectError: + isClientError: false + isTimeoutError: false + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: dropIndex + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + name: "x_1" + + expectError: + isClientError: false + isTimeoutError: false + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 5 + - name: dropIndexes + object: *collection + arguments: + timeoutMS: 100000 + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: dropIndexes + object: *collection + arguments: + timeoutMS: 100000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: dropIndexes + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + diff --git a/spec/spec_tests/data/client_side_operations_timeout/error-transformations.yml b/spec/spec_tests/data/client_side_operations_timeout/error-transformations.yml new file mode 100644 index 0000000000..7bff4776a8 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/error-transformations.yml @@ -0,0 +1,96 @@ +description: "MaxTimeMSExpired server errors are transformed into a custom timeout error" + +schemaVersion: "1.9" + +# failCommand is available on 4.0 for replica sets and 4.2 for sharded clusters. +runOnRequirements: + - minServerVersion: "4.0" + topologies: ["replicaset"] + - minServerVersion: "4.2" + topologies: ["sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # A server response like {ok: 0, code: 50, ...} is transformed. + - description: "basic MaxTimeMSExpired error is transformed" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + errorCode: 50 + - name: insertOne + object: *collection + arguments: + document: { _id: 1 } + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + + # A server response like {ok: 1, writeConcernError: {code: 50, ...}} is transformed. + - description: "write concern error MaxTimeMSExpired is transformed" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + writeConcernError: + code: 50 + errmsg: "maxTimeMS expired" + - name: insertOne + object: *collection + arguments: + document: { _id: 1 } + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } diff --git a/spec/spec_tests/data/client_side_operations_timeout/global-timeoutMS.yml b/spec/spec_tests/data/client_side_operations_timeout/global-timeoutMS.yml new file mode 100644 index 0000000000..7b4a78ac78 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/global-timeoutMS.yml @@ -0,0 +1,3236 @@ +# Tests in this file are generated from global-timeoutMS.yml.template. + +description: "timeoutMS can be configured on a MongoClient" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + +initialData: + - collectionName: &collectionName coll + databaseName: &databaseName test + documents: [] + +tests: + # For each operation, we execute two tests: + # + # 1. timeoutMS can be configured to a non-zero value on a MongoClient and is inherited by the operation. Each test + # constructs a client entity with timeoutMS=250 and configures a fail point to block the operation for 350ms so + # execution results in a timeout error. + # + # 2. timeoutMS can be set to 0 for a MongoClient. Each test constructs a client entity with timeoutMS=0 and + # configures a fail point to block the operation for 15ms. The tests expect the operation to succeed and the command + # sent to not contain a maxTimeMS field. + + - description: "timeoutMS can be configured on a MongoClient - listDatabases on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 350 + - name: listDatabases + object: *client + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - listDatabases on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 15 + - name: listDatabases + object: *client + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - listDatabaseNames on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 350 + - name: listDatabaseNames + object: *client + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - listDatabaseNames on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 15 + - name: listDatabaseNames + object: *client + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - createChangeStream on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 350 + - name: createChangeStream + object: *client + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - createChangeStream on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *client + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - aggregate on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 350 + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - aggregate on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - listCollections on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 350 + - name: listCollections + object: *database + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - listCollections on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollections + object: *database + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - listCollectionNames on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 350 + - name: listCollectionNames + object: *database + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - listCollectionNames on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollectionNames + object: *database + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - runCommand on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["ping"] + blockConnection: true + blockTimeMS: 350 + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: ping + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: ping + databaseName: *databaseName + command: + ping: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - runCommand on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["ping"] + blockConnection: true + blockTimeMS: 15 + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: ping + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: ping + databaseName: *databaseName + command: + ping: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - createChangeStream on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 350 + - name: createChangeStream + object: *database + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - createChangeStream on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *database + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 350 + - name: aggregate + object: *collection + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 350 + - name: count + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: count + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - countDocuments on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 350 + - name: countDocuments + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - countDocuments on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: countDocuments + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 350 + - name: estimatedDocumentCount + object: *collection + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: estimatedDocumentCount + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 350 + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 15 + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 350 + - name: find + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 350 + - name: findOne + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: findOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - listIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 350 + - name: listIndexes + object: *collection + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - listIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - listIndexNames on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 350 + - name: listIndexNames + object: *collection + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - listIndexNames on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexNames + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - createChangeStream on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 350 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - createChangeStream on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - insertOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 350 + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - insertOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - insertMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 350 + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - insertMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - deleteOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 350 + - name: deleteOne + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - deleteOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - deleteMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 350 + - name: deleteMany + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - deleteMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteMany + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - replaceOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 350 + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - replaceOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - updateOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 350 + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - updateOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - updateMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 350 + - name: updateMany + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - updateMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateMany + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 350 + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 350 + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 350 + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - bulkWrite on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 350 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - bulkWrite on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 350 + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 350 + - name: dropIndex + object: *collection + arguments: + name: "x_1" + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndex + object: *collection + arguments: + name: "x_1" + + expectError: + isClientError: false + isTimeoutError: false + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 350 + - name: dropIndexes + object: *collection + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$exists: false } + \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_operations_timeout/gridfs-advanced.yml b/spec/spec_tests/data/client_side_operations_timeout/gridfs-advanced.yml new file mode 100644 index 0000000000..b03812b719 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/gridfs-advanced.yml @@ -0,0 +1,207 @@ +description: "timeoutMS behaves correctly for advanced GridFS API operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid # GridFS ops can be slow on serverless. + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 75 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - bucket: + id: &bucket bucket + database: *database + - collection: + id: &filesCollection filesCollection + database: *database + collectionName: &filesCollectionName fs.files + - collection: + id: &chunksCollection chunksCollection + database: *database + collectionName: &chunksCollectionName fs.chunks + +initialData: + - collectionName: *filesCollectionName + databaseName: *databaseName + documents: + - _id: &fileDocumentId { $oid: "000000000000000000000005" } + length: 8 + chunkSize: 4 + uploadDate: { $date: "1970-01-01T00:00:00.000Z" } + filename: "length-8" + contentType: "application/octet-stream" + aliases: [] + metadata: {} + - collectionName: *chunksCollectionName + databaseName: *databaseName + documents: + - _id: { $oid: "000000000000000000000005" } + files_id: *fileDocumentId + n: 0 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex: 11223344 + - _id: { $oid: "000000000000000000000006" } + files_id: *fileDocumentId + n: 1 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex: 11223344 + +tests: + # Tests for the "rename" operation. + # Ruby driver does not support rename for GridFS bucket + + # - description: "timeoutMS can be overridden for a rename" + # operations: + # - name: failPoint + # object: testRunner + # arguments: + # client: *failPointClient + # failPoint: + # configureFailPoint: failCommand + # mode: { times: 1 } + # data: + # failCommands: ["update"] + # blockConnection: true + # blockTimeMS: 100 + # - name: rename + # object: *bucket + # arguments: + # id: *fileDocumentId + # newFilename: "foo" + # timeoutMS: 2000 # The client timeoutMS is 75ms and the operation blocks for 100ms, so 2000ms should let it succeed. + # expectEvents: + # - client: *client + # events: + # - commandStartedEvent: + # commandName: update + # databaseName: *databaseName + # command: + # update: *filesCollectionName + # maxTimeMS: { $$type: ["int", "long"] } + + # - description: "timeoutMS applied to update during a rename" + # operations: + # - name: failPoint + # object: testRunner + # arguments: + # client: *failPointClient + # failPoint: + # configureFailPoint: failCommand + # mode: { times: 1 } + # data: + # failCommands: ["update"] + # blockConnection: true + # blockTimeMS: 100 + # - name: rename + # object: *bucket + # arguments: + # id: *fileDocumentId + # newFilename: "foo" + # expectError: + # isTimeoutError: true + # expectEvents: + # - client: *client + # events: + # - commandStartedEvent: + # commandName: update + # databaseName: *databaseName + # command: + # update: *filesCollectionName + # maxTimeMS: { $$type: ["int", "long"] } + + # Tests for the "drop" operation. Any tests that might result in multiple commands being sent do not have expectEvents + # assertions as these assertions reduce test robustness and can cause flaky failures. + + - description: "timeoutMS can be overridden for drop" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["drop"] + blockConnection: true + blockTimeMS: 100 + - name: drop + object: *bucket + arguments: + timeoutMS: 2000 # The client timeoutMS is 75ms and the operation blocks for 100ms, so 2000ms should let it succeed. + + - description: "timeoutMS applied to files collection drop" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["drop"] + blockConnection: true + blockTimeMS: 100 + - name: drop + object: *bucket + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: drop + databaseName: *databaseName + command: + drop: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } + + - description: "timeoutMS applied to chunks collection drop" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: + # Skip the drop for the files collection. + skip: 1 + data: + failCommands: ["drop"] + blockConnection: true + blockTimeMS: 100 + - name: drop + object: *bucket + expectError: + isTimeoutError: true + + - description: "timeoutMS applied to drop as a whole, not individual parts" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["drop"] + blockConnection: true + blockTimeMS: 50 + - name: drop + object: *bucket + expectError: + isTimeoutError: true diff --git a/spec/spec_tests/data/client_side_operations_timeout/gridfs-delete.yml b/spec/spec_tests/data/client_side_operations_timeout/gridfs-delete.yml new file mode 100644 index 0000000000..9c72537c38 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/gridfs-delete.yml @@ -0,0 +1,152 @@ +description: "timeoutMS behaves correctly for GridFS delete operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid # GridFS ops can be slow on serverless. + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 75 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - bucket: + id: &bucket bucket + database: *database + - collection: + id: &filesCollection filesCollection + database: *database + collectionName: &filesCollectionName fs.files + - collection: + id: &chunksCollection chunksCollection + database: *database + collectionName: &chunksCollectionName fs.chunks + +initialData: + - collectionName: *filesCollectionName + databaseName: *databaseName + documents: + - _id: &fileDocumentId { $oid: "000000000000000000000005" } + length: 8 + chunkSize: 4 + uploadDate: { $date: "1970-01-01T00:00:00.000Z" } + filename: "length-8" + contentType: "application/octet-stream" + aliases: [] + metadata: {} + - collectionName: *chunksCollectionName + databaseName: *databaseName + documents: + - _id: { $oid: "000000000000000000000005" } + files_id: *fileDocumentId + n: 0 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex: 11223344 + - _id: { $oid: "000000000000000000000006" } + files_id: *fileDocumentId + n: 1 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex: 11223344 + +tests: + - description: "timeoutMS can be overridden for delete" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 100 + - name: delete + object: *bucket + arguments: + id: *fileDocumentId + timeoutMS: 1000 # The client timeoutMS is 75ms and the operation blocks for 100ms, so 1000ms should let it succeed. + + - description: "timeoutMS applied to delete against the files collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 100 + - name: delete + object: *bucket + arguments: + id: *fileDocumentId + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } + + - description: "timeoutMS applied to delete against the chunks collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: + # The first "delete" will be against the files collection, so we skip it. + skip: 1 + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 100 + - name: delete + object: *bucket + arguments: + id: *fileDocumentId + expectError: + isTimeoutError: true + + # Test that drivers are not refreshing the timeout between commands. We test this by blocking both "delete" commands + # for 50ms each. The delete should inherit timeoutMS=75 from the client/database and the server takes over 75ms + # total, so the operation should fail. + - description: "timeoutMS applied to entire delete, not individual parts" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 50 + - name: delete + object: *bucket + arguments: + id: *fileDocumentId + expectError: + isTimeoutError: true diff --git a/spec/spec_tests/data/client_side_operations_timeout/gridfs-download.yml b/spec/spec_tests/data/client_side_operations_timeout/gridfs-download.yml new file mode 100644 index 0000000000..772ffd6e08 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/gridfs-download.yml @@ -0,0 +1,182 @@ +description: "timeoutMS behaves correctly for GridFS download operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid # GridFS ops can be slow on serverless. + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 75 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - bucket: + id: &bucket bucket + database: *database + - collection: + id: &filesCollection filesCollection + database: *database + collectionName: &filesCollectionName fs.files + - collection: + id: &chunksCollection chunksCollection + database: *database + collectionName: &chunksCollectionName fs.chunks + +initialData: + - collectionName: *filesCollectionName + databaseName: *databaseName + documents: + - _id: &fileDocumentId { $oid: "000000000000000000000005" } + length: 8 + chunkSize: 4 + uploadDate: { $date: "1970-01-01T00:00:00.000Z" } + filename: "length-8" + contentType: "application/octet-stream" + aliases: [] + metadata: {} + - collectionName: *chunksCollectionName + databaseName: *databaseName + documents: + - _id: { $oid: "000000000000000000000005" } + files_id: *fileDocumentId + n: 0 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex: 11223344 + - _id: { $oid: "000000000000000000000006" } + files_id: *fileDocumentId + n: 1 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex: 11223344 + +tests: + - description: "timeoutMS can be overridden for download" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 100 + - name: download + object: *bucket + arguments: + id: *fileDocumentId + timeoutMS: 1000 # The client timeoutMS is 75ms and the operation blocks for 100ms, so 1000ms should let it succeed. + + - description: "timeoutMS applied to find to get files document" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 100 + - name: download + object: *bucket + arguments: + id: *fileDocumentId + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } + + - description: "timeoutMS applied to find to get chunks" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: + # The first "find" will be against the files collection, so we skip it. + skip: 1 + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 100 + - name: download + object: *bucket + arguments: + id: *fileDocumentId + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *chunksCollectionName + maxTimeMS: { $$type: ["int", "long"] } + + # Test that drivers are not refreshing the timeout between commands. We test this by blocking both "find" commands + # for 50ms each. The download should inherit timeoutMS=75 from the client/database and the server takes over 75ms + # total, so the operation should fail. + - description: "timeoutMS applied to entire download, not individual parts" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 50 + - name: download + object: *bucket + arguments: + id: *fileDocumentId + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *chunksCollectionName + maxTimeMS: { $$type: ["int", "long"] } diff --git a/spec/spec_tests/data/client_side_operations_timeout/gridfs-find.yml b/spec/spec_tests/data/client_side_operations_timeout/gridfs-find.yml new file mode 100644 index 0000000000..000150ae67 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/gridfs-find.yml @@ -0,0 +1,100 @@ +description: "timeoutMS behaves correctly for GridFS find operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid # GridFS ops can be slow on serverless. + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 75 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - bucket: + id: &bucket bucket + database: *database + - collection: + id: &filesCollection filesCollection + database: *database + collectionName: &filesCollectionName fs.files + - collection: + id: &chunksCollection chunksCollection + database: *database + collectionName: &chunksCollectionName fs.chunks + +initialData: + - collectionName: *filesCollectionName + databaseName: *databaseName + documents: [] + - collectionName: *chunksCollectionName + databaseName: *databaseName + documents: [] + +tests: + - description: "timeoutMS can be overridden for a find" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 100 + - name: find + object: *bucket + arguments: + filter: {} + timeoutMS: 1000 # The client timeoutMS is 75ms and the operation blocks for 100ms, so 1000ms should let it succeed. + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } + + - description: "timeoutMS applied to find command" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 100 + - name: find + object: *bucket + arguments: + filter: {} + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } diff --git a/spec/spec_tests/data/client_side_operations_timeout/gridfs-upload.yml b/spec/spec_tests/data/client_side_operations_timeout/gridfs-upload.yml new file mode 100644 index 0000000000..51e1366878 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/gridfs-upload.yml @@ -0,0 +1,249 @@ +description: "timeoutMS behaves correctly for GridFS upload operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid # GridFS ops can be slow on serverless. + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 75 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: &databaseName test + - bucket: + id: &bucket bucket + database: *database + - collection: + id: &filesCollection filesCollection + database: *database + collectionName: &filesCollectionName fs.files + - collection: + id: &chunksCollection chunksCollection + database: *database + collectionName: &chunksCollectionName fs.chunks + +initialData: + - collectionName: *filesCollectionName + databaseName: *databaseName + documents: [] + - collectionName: *chunksCollectionName + databaseName: *databaseName + documents: [] + +tests: + # Many tests in this file do not specify command monitoring expectations because GridFS uploads internally do a + # number of operations, so expecting an exact set of commands can cause flaky failures. + + - description: "timeoutMS can be overridden for upload" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + timeoutMS: 1000 + + # On the first write to the bucket, drivers check if the files collection is empty to see if indexes need to be + # created. + - description: "timeoutMS applied to initial find on files collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true + + # On the first write to the bucket, drivers check if the files collection has the correct indexes. + - description: "timeoutMS applied to listIndexes on files collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true + + # If the files collection is empty when the first write to the bucket occurs, drivers attempt to create an index + # on the bucket's files collection. + - description: "timeoutMS applied to index creation for files collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true + + # On the first write to the bucket, drivers check if the chunks collection has the correct indexes. + - description: "timeoutMS applied to listIndexes on chunks collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # The first listIndexes will be on the files collection, so we skip it. + mode: { skip: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true + + # If the files collection is empty when the first write to the bucket occurs, drivers attempt to create an index + # on the bucket's chunks collection. + - description: "timeoutMS applied to index creation for chunks collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # This index is created after the one on the files collection, so we skip the first createIndexes command + # and target the second. + mode: { skip: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true + + - description: "timeoutMS applied to chunk insertion" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true + + - description: "timeoutMS applied to creation of files document" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Skip the insert to upload the chunk. Because the whole file fits into one chunk, the second insert will + # be the files document upload. + mode: { skip: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true + + # Test that drivers apply timeoutMS to the entire upload rather than refreshing it between individual commands. We + # test this by blocking the "find" and "listIndexes" commands for 50ms each and performing an upload. The upload + # should inherit timeoutMS=75 from the client/database and the server takes over 75ms total, so the operation should + # fail. + - description: "timeoutMS applied to upload as a whole, not individual parts" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find", "listIndexes"] + blockConnection: true + blockTimeMS: 50 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true diff --git a/spec/spec_tests/data/client_side_operations_timeout/legacy-timeouts.yml b/spec/spec_tests/data/client_side_operations_timeout/legacy-timeouts.yml new file mode 100644 index 0000000000..81c48f7c4f --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/legacy-timeouts.yml @@ -0,0 +1,204 @@ +description: "legacy timeouts continue to work if timeoutMS is not set" + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "4.4" + +initialData: + - collectionName: &collectionName coll + databaseName: &databaseName test + documents: [] + +tests: + - description: "socketTimeoutMS is not used to derive a maxTimeMS command field" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + uriOptions: + socketTimeoutMS: 50000 + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + + - description: "waitQueueTimeoutMS is not used to derive a maxTimeMS command field" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + uriOptions: + waitQueueTimeoutMS: 50000 + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + + - description: "wTimeoutMS is not used to derive a maxTimeMS command field" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + uriOptions: + wTimeoutMS: &wTimeoutMS 50000 + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + writeConcern: + wtimeout: *wTimeoutMS + + # If the maxTimeMS option is set for a specific command, it should be used as the maxTimeMS command field without any + # modifications. This is different from timeoutMS because in that case, drivers subtract the target server's min + # RTT from the remaining timeout to derive a maxTimeMS field. + - description: "maxTimeMS option is used directly as the maxTimeMS field on a command" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: estimatedDocumentCount + object: *collection + arguments: + maxTimeMS: &maxTimeMS 50000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: *maxTimeMS + + # Same test as above but with the maxCommitTimeMS option. + - description: "maxCommitTimeMS option is used directly as the maxTimeMS field on a commitTransaction command" + runOnRequirements: + # Note: minServerVersion is specified in top-level runOnRequirements + - topologies: ["replicaset", "sharded"] + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + sessionOptions: + defaultTransactionOptions: + maxCommitTimeMS: &maxCommitTimeMS 1000 + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + document: { _id: 1 } + session: *session + - name: commitTransaction + object: *session + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + maxTimeMS: *maxCommitTimeMS diff --git a/spec/spec_tests/data/client_side_operations_timeout/non-tailable-cursors.yml b/spec/spec_tests/data/client_side_operations_timeout/non-tailable-cursors.yml new file mode 100644 index 0000000000..4862ba21a9 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/non-tailable-cursors.yml @@ -0,0 +1,307 @@ +description: "timeoutMS behaves correctly for non-tailable cursors" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 10 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 0 } + - { _id: 1 } + - { _id: 2 } + - collectionName: &aggregateOutputCollectionName aggregateOutputColl + databaseName: *databaseName + documents: [] + +tests: + # If timeoutMode is explicitly set to CURSOR_LIFETIME, the timeout should apply to the initial command. + # This should also be the case if timeoutMode is unset, but this is already tested in global-timeoutMS.yml. + - description: "timeoutMS applied to find if timeoutMode is cursor_lifetime" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + # changed to 30ms to accommodate jruby latencies + blockTimeMS: 30 + - name: find + object: *collection + arguments: + filter: {} + # added as a 25ms timeout to accommodate jruby latencies + timeoutMS: 25 + timeoutMode: cursorLifetime + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + + # If timeoutMode is unset, it should default to CURSOR_LIFETIME and the time remaining after the find succeeds should + # be applied to the getMore. + - description: "remaining timeoutMS applied to getMore if timeoutMode is unset" + operations: + # Block find/getMore for 15ms. + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find", "getMore"] + blockConnection: true + # bumped to 50 to accommodate jruby latencies + blockTimeMS: 50 + # Run a find with timeoutMS=39 and batchSize=1 to force two batches, which will cause a find and a getMore to be + # sent. Both will block for 20ms so together they will go over the timeout. + - name: find + object: *collection + arguments: + filter: {} + # bumped to 99 to accommodate jruby latencies + timeoutMS: 99 + batchSize: 2 + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + + # Same test as above, but with timeoutMode explicitly set to CURSOR_LIFETIME. + - description: "remaining timeoutMS applied to getMore if timeoutMode is cursor_lifetime" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find", "getMore"] + blockConnection: true + blockTimeMS: 20 + - name: find + object: *collection + arguments: + filter: {} + timeoutMode: cursorLifetime + timeoutMS: 39 + batchSize: 2 + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + + # If timeoutMode=ITERATION, timeoutMS should apply to the initial find command and the command shouldn't have a + # maxTimeMS field. + - description: "timeoutMS applied to find if timeoutMode is iteration" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + filter: {} + timeoutMode: iteration + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + + # If timeoutMode=ITERATION, timeoutMS applies separately to the initial find and the getMore on the cursor. Neither + # command should have a maxTimeMS field. This is a success test. The "find" is executed with timeoutMS=29 and both + # "find" and "getMore" commands are blocked for 15ms each. Neither exceeds the timeout, so iteration succeeds. + - description: "timeoutMS is refreshed for getMore if timeoutMode is iteration - success" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find", "getMore"] + blockConnection: true + # blockTimeMS: 15 + # Increase timeout + blockTimeMS: 20 + - name: find + object: *collection + arguments: + filter: {} + timeoutMode: iteration + # timeoutMS: 29 + # Increase timeout + timeoutMS: 39 + batchSize: 2 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + + # If timeoutMode=ITERATION, timeoutMS applies separately to the initial find and the getMore on the cursor. Neither + # command should have a maxTimeMS field. This is a failure test. The "find" inherits timeoutMS=10 and "getMore" + # commands are blocked for 15ms, causing iteration to fail with a timeout error. + - description: "timeoutMS is refreshed for getMore if timeoutMode is iteration - failure" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + filter: {} + timeoutMode: iteration + batchSize: 2 + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + + - description: "aggregate with $out errors if timeoutMode is iteration" + operations: + - name: aggregate + object: *collection + arguments: + pipeline: + - $out: *aggregateOutputCollectionName + timeoutMS: 100 + timeoutMode: iteration + expectError: + isClientError: true + expectEvents: + - client: *client + events: [] + + - description: "aggregate with $merge errors if timeoutMode is iteration" + operations: + - name: aggregate + object: *collection + arguments: + pipeline: + - $merge: *aggregateOutputCollectionName + timeoutMS: 100 + timeoutMode: iteration + expectError: + isClientError: true + expectEvents: + - client: *client + events: [] diff --git a/spec/spec_tests/data/client_side_operations_timeout/override-collection-timeoutMS.yml b/spec/spec_tests/data/client_side_operations_timeout/override-collection-timeoutMS.yml new file mode 100644 index 0000000000..d1d1c61056 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/override-collection-timeoutMS.yml @@ -0,0 +1,1877 @@ +# Tests in this file are generated from override-collection-timeoutMS.yml.template. + +description: "timeoutMS can be overridden for a MongoCollection" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 10 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: &databaseName test + +initialData: + - collectionName: &collectionName coll + databaseName: *databaseName + documents: [] + +tests: + # For each collection-level operation, we execute two tests: + # + # 1. timeoutMS can be overridden to a non-zero value for a MongoCollection. Each test uses the client entity defined + # above to construct a collection entity with timeoutMS=1000 and configures a fail point to block the operation for + # 15ms so the operation succeeds. + # + # 2. timeoutMS can be overridden to 0 for a MongoCollection. Each test constructs a collection entity with + # timeoutMS=0 using the global client entity and configures a fail point to block the operation for 15ms. The + # operation should succeed and the command sent to the server should not contain a maxTimeMS field. + + - description: "timeoutMS can be configured on a MongoCollection - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: count + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: count + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - countDocuments on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: countDocuments + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - countDocuments on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: countDocuments + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: estimatedDocumentCount + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: estimatedDocumentCount + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 15 + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 15 + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: findOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: findOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - listIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - listIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - listIndexNames on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexNames + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - listIndexNames on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexNames + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - createChangeStream on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - createChangeStream on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - insertOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - insertOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - insertMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - insertMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - deleteOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - deleteOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - deleteMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteMany + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - deleteMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteMany + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - replaceOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - replaceOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - updateOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - updateOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - updateMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateMany + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - updateMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateMany + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - bulkWrite on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - bulkWrite on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndex + object: *collection + arguments: + name: "x_1" + + expectError: + isClientError: false + isTimeoutError: false + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndex + object: *collection + arguments: + name: "x_1" + + expectError: + isClientError: false + isTimeoutError: false + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$exists: false } + \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_operations_timeout/override-operation-timeoutMS.yml b/spec/spec_tests/data/client_side_operations_timeout/override-operation-timeoutMS.yml new file mode 100644 index 0000000000..28eabcb7c8 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/override-operation-timeoutMS.yml @@ -0,0 +1,1918 @@ +# Tests in this file are generated from override-operation-timeoutMS.yml.template. + +description: "timeoutMS can be overridden for an operation" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 10 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # For each level operation, we execute two tests: + # + # 1. timeoutMS can be overridden to a non-zero value for an operation. Each test executes an operation using one of + # the entities defined above with an overridden timeoutMS=1000 and configures a fail point to block the operation for + # 15ms so the operation succeeds. + # + # 2. timeoutMS can be overridden to 0 for an operation. Each test executes an operation using the entities defined + # above with an overridden timeoutMS=0 so the operation succeeds. + + - description: "timeoutMS can be configured for an operation - listDatabases on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 15 + - name: listDatabases + object: *client + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - listDatabases on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 15 + - name: listDatabases + object: *client + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - listDatabaseNames on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 15 + - name: listDatabaseNames + object: *client + arguments: + timeoutMS: 1000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - listDatabaseNames on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 15 + - name: listDatabaseNames + object: *client + arguments: + timeoutMS: 0 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - createChangeStream on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *client + arguments: + timeoutMS: 1000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - createChangeStream on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *client + arguments: + timeoutMS: 0 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - aggregate on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *database + arguments: + timeoutMS: 1000 + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - aggregate on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *database + arguments: + timeoutMS: 0 + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - listCollections on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollections + object: *database + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - listCollections on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollections + object: *database + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - listCollectionNames on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollectionNames + object: *database + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - listCollectionNames on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollectionNames + object: *database + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - runCommand on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["ping"] + blockConnection: true + blockTimeMS: 15 + - name: runCommand + object: *database + arguments: + timeoutMS: 1000 + command: { ping: 1 } + commandName: ping + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: ping + databaseName: *databaseName + command: + ping: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - runCommand on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["ping"] + blockConnection: true + blockTimeMS: 15 + - name: runCommand + object: *database + arguments: + timeoutMS: 0 + command: { ping: 1 } + commandName: ping + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: ping + databaseName: *databaseName + command: + ping: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - createChangeStream on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *database + arguments: + timeoutMS: 1000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - createChangeStream on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *database + arguments: + timeoutMS: 0 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - aggregate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *collection + arguments: + timeoutMS: 1000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - aggregate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *collection + arguments: + timeoutMS: 0 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - count on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: count + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - count on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: count + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - countDocuments on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: countDocuments + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - countDocuments on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: countDocuments + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - estimatedDocumentCount on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: estimatedDocumentCount + object: *collection + arguments: + timeoutMS: 1000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - estimatedDocumentCount on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: estimatedDocumentCount + object: *collection + arguments: + timeoutMS: 0 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - distinct on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 15 + - name: distinct + object: *collection + arguments: + timeoutMS: 1000 + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - distinct on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 15 + - name: distinct + object: *collection + arguments: + timeoutMS: 0 + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - find on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - find on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - findOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: findOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - findOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: findOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - listIndexes on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexes + object: *collection + arguments: + timeoutMS: 1000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - listIndexes on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexes + object: *collection + arguments: + timeoutMS: 0 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - listIndexNames on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexNames + object: *collection + arguments: + timeoutMS: 1000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - listIndexNames on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexNames + object: *collection + arguments: + timeoutMS: 0 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - createChangeStream on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *collection + arguments: + timeoutMS: 1000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - createChangeStream on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *collection + arguments: + timeoutMS: 0 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - insertOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertOne + object: *collection + arguments: + timeoutMS: 1000 + document: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - insertOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertOne + object: *collection + arguments: + timeoutMS: 0 + document: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - insertMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertMany + object: *collection + arguments: + timeoutMS: 1000 + documents: + - { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - insertMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertMany + object: *collection + arguments: + timeoutMS: 0 + documents: + - { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - deleteOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - deleteOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - deleteMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteMany + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - deleteMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteMany + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - replaceOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: replaceOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - replaceOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: replaceOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - updateOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - updateOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - updateMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateMany + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - updateMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateMany + object: *collection + arguments: + timeoutMS: 0 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - findOneAndDelete on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndDelete + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - findOneAndDelete on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndDelete + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - findOneAndReplace on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndReplace + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - findOneAndReplace on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndReplace + object: *collection + arguments: + timeoutMS: 0 + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - findOneAndUpdate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndUpdate + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - findOneAndUpdate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndUpdate + object: *collection + arguments: + timeoutMS: 0 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - bulkWrite on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: bulkWrite + object: *collection + arguments: + timeoutMS: 1000 + requests: + - insertOne: + document: { _id: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - bulkWrite on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: bulkWrite + object: *collection + arguments: + timeoutMS: 0 + requests: + - insertOne: + document: { _id: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - createIndex on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: createIndex + object: *collection + arguments: + timeoutMS: 1000 + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - createIndex on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: createIndex + object: *collection + arguments: + timeoutMS: 0 + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - dropIndex on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndex + object: *collection + arguments: + timeoutMS: 1000 + name: "x_1" + + expectError: + isTimeoutError: false # IndexNotFound + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - dropIndex on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndex + object: *collection + arguments: + timeoutMS: 0 + name: "x_1" + + expectError: + isTimeoutError: false # IndexNotFound + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - dropIndexes on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndexes + object: *collection + arguments: + timeoutMS: 1000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - dropIndexes on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndexes + object: *collection + arguments: + timeoutMS: 0 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$exists: false } + \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_operations_timeout/retryability-legacy-timeouts.yml b/spec/spec_tests/data/client_side_operations_timeout/retryability-legacy-timeouts.yml new file mode 100644 index 0000000000..abcaec6127 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/retryability-legacy-timeouts.yml @@ -0,0 +1,1676 @@ +# Tests in this file are generated from retryability-legacy-timeouts.yml.template. + +description: "legacy timeouts behave correctly for retryable operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + socketTimeoutMS: 100 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # For each retryable operation, run two tests: + # + # 1. Socket timeouts are retried once - Each test constructs a client entity with socketTimeoutMS=100, configures a + # fail point to block the operation once for 125ms, and expects the operation to succeed. + # + # 2. Operations fail after two consecutive socket timeouts - Same as (1) but the fail point is configured to block + # the operation twice and the test expects the operation to fail. + + - description: "operation succeeds after one socket timeout - insertOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 125 + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - description: "operation fails after two consecutive socket timeouts - insertOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 125 + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - description: "operation succeeds after one socket timeout - insertMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 125 + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - description: "operation fails after two consecutive socket timeouts - insertMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 125 + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - description: "operation succeeds after one socket timeout - deleteOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 125 + - name: deleteOne + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + - description: "operation fails after two consecutive socket timeouts - deleteOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 125 + - name: deleteOne + object: *collection + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + - description: "operation succeeds after one socket timeout - replaceOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 125 + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - description: "operation fails after two consecutive socket timeouts - replaceOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 125 + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - description: "operation succeeds after one socket timeout - updateOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 125 + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - description: "operation fails after two consecutive socket timeouts - updateOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 125 + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - description: "operation succeeds after one socket timeout - findOneAndDelete on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 125 + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - description: "operation fails after two consecutive socket timeouts - findOneAndDelete on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 125 + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - description: "operation succeeds after one socket timeout - findOneAndReplace on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 125 + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - description: "operation fails after two consecutive socket timeouts - findOneAndReplace on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 125 + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - description: "operation succeeds after one socket timeout - findOneAndUpdate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 125 + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - description: "operation fails after two consecutive socket timeouts - findOneAndUpdate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 125 + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - description: "operation succeeds after one socket timeout - bulkWrite on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 125 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - description: "operation fails after two consecutive socket timeouts - bulkWrite on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 125 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - description: "operation succeeds after one socket timeout - listDatabases on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 125 + - name: listDatabases + object: *client + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - description: "operation fails after two consecutive socket timeouts - listDatabases on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 125 + - name: listDatabases + object: *client + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - description: "operation succeeds after one socket timeout - listDatabaseNames on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 125 + - name: listDatabaseNames + object: *client + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - description: "operation fails after two consecutive socket timeouts - listDatabaseNames on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 125 + - name: listDatabaseNames + object: *client + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - description: "operation succeeds after one socket timeout - createChangeStream on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: createChangeStream + object: *client + arguments: + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + - description: "operation fails after two consecutive socket timeouts - createChangeStream on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: createChangeStream + object: *client + arguments: + pipeline: [] + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + - description: "operation succeeds after one socket timeout - aggregate on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - description: "operation fails after two consecutive socket timeouts - aggregate on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - description: "operation succeeds after one socket timeout - listCollections on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 125 + - name: listCollections + object: *database + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - description: "operation fails after two consecutive socket timeouts - listCollections on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 125 + - name: listCollections + object: *database + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - description: "operation succeeds after one socket timeout - listCollectionNames on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 125 + - name: listCollectionNames + object: *database + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - description: "operation fails after two consecutive socket timeouts - listCollectionNames on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 125 + - name: listCollectionNames + object: *database + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - description: "operation succeeds after one socket timeout - createChangeStream on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: createChangeStream + object: *database + arguments: + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - description: "operation fails after two consecutive socket timeouts - createChangeStream on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: createChangeStream + object: *database + arguments: + pipeline: [] + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - description: "operation succeeds after one socket timeout - aggregate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: aggregate + object: *collection + arguments: + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - description: "operation fails after two consecutive socket timeouts - aggregate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: aggregate + object: *collection + arguments: + pipeline: [] + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - description: "operation succeeds after one socket timeout - count on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 125 + - name: count + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - description: "operation fails after two consecutive socket timeouts - count on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 125 + - name: count + object: *collection + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - description: "operation succeeds after one socket timeout - countDocuments on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: countDocuments + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - description: "operation fails after two consecutive socket timeouts - countDocuments on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: countDocuments + object: *collection + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - description: "operation succeeds after one socket timeout - estimatedDocumentCount on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 125 + - name: estimatedDocumentCount + object: *collection + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - description: "operation fails after two consecutive socket timeouts - estimatedDocumentCount on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 125 + - name: estimatedDocumentCount + object: *collection + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - description: "operation succeeds after one socket timeout - distinct on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 125 + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + - description: "operation fails after two consecutive socket timeouts - distinct on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 125 + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + - description: "operation succeeds after one socket timeout - find on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 125 + - name: find + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - description: "operation fails after two consecutive socket timeouts - find on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 125 + - name: find + object: *collection + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - description: "operation succeeds after one socket timeout - findOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 125 + - name: findOne + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - description: "operation fails after two consecutive socket timeouts - findOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 125 + - name: findOne + object: *collection + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - description: "operation succeeds after one socket timeout - listIndexes on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 125 + - name: listIndexes + object: *collection + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + - description: "operation fails after two consecutive socket timeouts - listIndexes on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 125 + - name: listIndexes + object: *collection + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + - description: "operation succeeds after one socket timeout - createChangeStream on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - description: "operation fails after two consecutive socket timeouts - createChangeStream on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_operations_timeout/retryability-timeoutMS.yml b/spec/spec_tests/data/client_side_operations_timeout/retryability-timeoutMS.yml new file mode 100644 index 0000000000..6f47d6c2e4 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/retryability-timeoutMS.yml @@ -0,0 +1,2824 @@ +# Tests in this file are generated from retryability-timeoutMS.yml.template. + +description: "timeoutMS behaves correctly for retryable operations" + +schemaVersion: "1.9" + +# failCommand is available on 4.0+ replica sets and 4.2+ sharded clusters. +runOnRequirements: + - minServerVersion: "4.0" + topologies: ["replicaset"] + - minServerVersion: "4.2" + topologies: ["sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 100 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # For each retryable operation, run three tests: + # + # 1. timeoutMS applies to the whole operation, not to individual attempts - Client timeoutMS=100 and the operation is + # fails with a retryable error after being blocked server-side for 60ms. The operation should fail with a timeout error + # because the second attempt should take it over the 100ms limit. This test only runs on 4.4+ because it uses the + # blockConnection option in failCommand. + # + # 2. operation is retried multiple times if timeoutMS is set to a non-zero value - Client timeoutMS=100 and the + # operation fails with a retryable error twice. Drivers should send the original operation and two retries, the + # second of which should succeed. + # + # 3. operation is retried multiple times if timeoutMS is set to a zero - Override timeoutMS to zero for the operation + # and set a fail point to force a retryable error twice. Drivers should send the original operation and two retries, + # the second of which should succeed. + # + # The fail points in these tests use error code 7 (HostNotFound) because it is a retryable error but does not trigger + # an SDAM state change so we don't lose any time to server rediscovery. The tests also explicitly specify an + # errorLabels array in the fail point to avoid behavioral differences among server types and ensure that the error + # will be considered retryable. + + - description: "timeoutMS applies to whole operation, not individual attempts - insertOne on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - insertOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: insertOne + object: *collection + arguments: + timeoutMS: 1000 + document: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - insertOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: insertOne + object: *collection + arguments: + timeoutMS: 0 + document: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - insertMany on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - insertMany on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: insertMany + object: *collection + arguments: + timeoutMS: 1000 + documents: + - { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - insertMany on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: insertMany + object: *collection + arguments: + timeoutMS: 0 + documents: + - { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - deleteOne on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: deleteOne + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - deleteOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["delete"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: deleteOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - deleteOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["delete"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: deleteOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - replaceOne on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - replaceOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["update"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: replaceOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + replacement: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - replaceOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["update"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: replaceOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + replacement: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - updateOne on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - updateOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["update"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: updateOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + update: { $set: { x: 1 } } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - updateOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["update"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: updateOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + update: { $set: { x: 1 } } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - findOneAndDelete on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - findOneAndDelete on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOneAndDelete + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - findOneAndDelete on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOneAndDelete + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - findOneAndReplace on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - findOneAndReplace on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOneAndReplace + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + replacement: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - findOneAndReplace on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOneAndReplace + object: *collection + arguments: + timeoutMS: 0 + filter: {} + replacement: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - findOneAndUpdate on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - findOneAndUpdate on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOneAndUpdate + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + update: { $set: { x: 1 } } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - findOneAndUpdate on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOneAndUpdate + object: *collection + arguments: + timeoutMS: 0 + filter: {} + update: { $set: { x: 1 } } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - bulkWrite on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - bulkWrite on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: bulkWrite + object: *collection + arguments: + timeoutMS: 1000 + requests: + - insertOne: + document: { _id: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - bulkWrite on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: bulkWrite + object: *collection + arguments: + timeoutMS: 0 + requests: + - insertOne: + document: { _id: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - listDatabases on client" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: listDatabases + object: *client + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - listDatabases on client" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listDatabases + object: *client + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - listDatabases on client" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listDatabases + object: *client + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - listDatabaseNames on client" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: listDatabaseNames + object: *client + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - listDatabaseNames on client" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listDatabaseNames + object: *client + arguments: + timeoutMS: 1000 + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - listDatabaseNames on client" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listDatabaseNames + object: *client + arguments: + timeoutMS: 0 + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - createChangeStream on client" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *client + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - createChangeStream on client" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *client + arguments: + timeoutMS: 1000 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - createChangeStream on client" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *client + arguments: + timeoutMS: 0 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - aggregate on database" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - aggregate on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: aggregate + object: *database + arguments: + timeoutMS: 1000 + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - aggregate on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: aggregate + object: *database + arguments: + timeoutMS: 0 + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - listCollections on database" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: listCollections + object: *database + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - listCollections on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listCollections"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listCollections + object: *database + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - listCollections on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listCollections"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listCollections + object: *database + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - listCollectionNames on database" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: listCollectionNames + object: *database + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - listCollectionNames on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listCollections"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listCollectionNames + object: *database + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - listCollectionNames on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listCollections"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listCollectionNames + object: *database + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - createChangeStream on database" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *database + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - createChangeStream on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *database + arguments: + timeoutMS: 1000 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - createChangeStream on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *database + arguments: + timeoutMS: 0 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - aggregate on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: aggregate + object: *collection + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - aggregate on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: aggregate + object: *collection + arguments: + timeoutMS: 1000 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - aggregate on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: aggregate + object: *collection + arguments: + timeoutMS: 0 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - count on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: count + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - count on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["count"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: count + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - count on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["count"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: count + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - countDocuments on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: countDocuments + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - countDocuments on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: countDocuments + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - countDocuments on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: countDocuments + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - estimatedDocumentCount on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: estimatedDocumentCount + object: *collection + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - estimatedDocumentCount on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["count"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: estimatedDocumentCount + object: *collection + arguments: + timeoutMS: 1000 + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - estimatedDocumentCount on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["count"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: estimatedDocumentCount + object: *collection + arguments: + timeoutMS: 0 + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - distinct on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - distinct on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["distinct"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: distinct + object: *collection + arguments: + timeoutMS: 1000 + fieldName: x + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - distinct on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["distinct"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: distinct + object: *collection + arguments: + timeoutMS: 0 + fieldName: x + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - find on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: find + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - find on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: find + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - find on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: find + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - findOne on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: findOne + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - findOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - findOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - listIndexes on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: listIndexes + object: *collection + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - listIndexes on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listIndexes"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listIndexes + object: *collection + arguments: + timeoutMS: 1000 + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - listIndexes on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listIndexes"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listIndexes + object: *collection + arguments: + timeoutMS: 0 + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - createChangeStream on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - createChangeStream on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *collection + arguments: + timeoutMS: 1000 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - createChangeStream on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *collection + arguments: + timeoutMS: 0 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + \ No newline at end of file diff --git a/spec/spec_tests/data/client_side_operations_timeout/sessions-inherit-timeoutMS.yml b/spec/spec_tests/data/client_side_operations_timeout/sessions-inherit-timeoutMS.yml new file mode 100644 index 0000000000..184ef7eb9e --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/sessions-inherit-timeoutMS.yml @@ -0,0 +1,168 @@ +description: "sessions inherit timeoutMS from their parent MongoClient" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 50 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + - session: + id: &session session + client: *client + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # Drivers ignore errors from abortTransaction, so the tests in this file use commandSucceededEvent and + # commandFailedEvent events to assert success/failure. + + - description: "timeoutMS applied to commitTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["commitTransaction"] + blockConnection: true + blockTimeMS: 60 + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + - name: commitTransaction + object: *session + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandSucceededEvent: + commandName: insert + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: commitTransaction + + - description: "timeoutMS applied to abortTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["abortTransaction"] + blockConnection: true + blockTimeMS: 60 + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + - name: abortTransaction + object: *session + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandSucceededEvent: + commandName: insert + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: abortTransaction + + - description: "timeoutMS applied to withTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 60 + - name: withTransaction + object: *session + arguments: + callback: + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + expectError: + isTimeoutError: true + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + # Because the insert expects an error and gets an error, it technically succeeds, so withTransaction will + # try to run commitTransaction. This will fail client-side, though, because the timeout has already expired, + # so no command is sent. + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + # withTransaction specifies timeoutMS for each operation in the callback that uses the session, so the + # insert command should have a maxTimeMS field. + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: insert diff --git a/spec/spec_tests/data/client_side_operations_timeout/sessions-override-operation-timeoutMS.yml b/spec/spec_tests/data/client_side_operations_timeout/sessions-override-operation-timeoutMS.yml new file mode 100644 index 0000000000..8a80a65720 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/sessions-override-operation-timeoutMS.yml @@ -0,0 +1,171 @@ +description: "timeoutMS can be overridden for individual session operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + - session: + id: &session session + client: *client + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # Drivers ignore errors from abortTransaction, so the tests in this file use commandSucceededEvent and + # commandFailedEvent events to assert success/failure. + + - description: "timeoutMS can be overridden for commitTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["commitTransaction"] + blockConnection: true + blockTimeMS: 60 + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + - name: commitTransaction + object: *session + arguments: + timeoutMS: 50 + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandSucceededEvent: + commandName: insert + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: commitTransaction + + - description: "timeoutMS applied to abortTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["abortTransaction"] + blockConnection: true + blockTimeMS: 60 + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + - name: abortTransaction + object: *session + arguments: + timeoutMS: 50 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandSucceededEvent: + commandName: insert + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: abortTransaction + + - description: "timeoutMS applied to withTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 60 + - name: withTransaction + object: *session + arguments: + timeoutMS: 50 + callback: + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + expectError: + isTimeoutError: true + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + # Because the insert expects an error and gets an error, it technically succeeds, so withTransaction will + # try to run commitTransaction. This will fail client-side, though, because the timeout has already expired, + # so no command is sent. + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + # withTransaction specifies timeoutMS for each operation in the callback that uses the session, so the + # insert command should have a maxTimeMS field. + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: insert diff --git a/spec/spec_tests/data/client_side_operations_timeout/sessions-override-timeoutMS.yml b/spec/spec_tests/data/client_side_operations_timeout/sessions-override-timeoutMS.yml new file mode 100644 index 0000000000..61aaab4d97 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/sessions-override-timeoutMS.yml @@ -0,0 +1,168 @@ +description: "timeoutMS can be overridden at the level of a ClientSession" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + - session: + id: &session session + client: *client + sessionOptions: + defaultTimeoutMS: 50 + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # Drivers ignore errors from abortTransaction, so the tests in this file use commandSucceededEvent and + # commandFailedEvent events to assert success/failure. + + - description: "timeoutMS applied to commitTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["commitTransaction"] + blockConnection: true + blockTimeMS: 60 + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + - name: commitTransaction + object: *session + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandSucceededEvent: + commandName: insert + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: commitTransaction + + - description: "timeoutMS applied to abortTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["abortTransaction"] + blockConnection: true + blockTimeMS: 60 + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + - name: abortTransaction + object: *session + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandSucceededEvent: + commandName: insert + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: abortTransaction + + - description: "timeoutMS applied to withTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 60 + - name: withTransaction + object: *session + arguments: + callback: + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + expectError: + isTimeoutError: true + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + # Because the insert expects an error and gets an error, it technically succeeds, so withTransaction will + # try to run commitTransaction. This will fail client-side, though, because the timeout has already expired, + # so no command is sent. + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + # withTransaction specifies timeoutMS for each operation in the callback that uses the session, so the + # insert command should have a maxTimeMS field. + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: insert diff --git a/spec/spec_tests/data/client_side_operations_timeout/tailable-awaitData.yml b/spec/spec_tests/data/client_side_operations_timeout/tailable-awaitData.yml new file mode 100644 index 0000000000..9f5790943d --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/tailable-awaitData.yml @@ -0,0 +1,247 @@ +description: "timeoutMS behaves correctly for tailable awaitData cursors" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 10 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + createOptions: + capped: true + size: 500 + documents: + - { _id: 0 } + - { _id: 1 } + +tests: + - description: "error if timeoutMode is cursor_lifetime" + operations: + - name: find + object: *collection + arguments: + filter: {} + timeoutMode: cursorLifetime + cursorType: tailableAwait + expectError: + isClientError: true + + - description: "error if maxAwaitTimeMS is greater than timeoutMS" + operations: + - name: find + object: *collection + arguments: + filter: {} + cursorType: tailableAwait + timeoutMS: 5 + maxAwaitTimeMS: 10 + expectError: + isClientError: true + + - description: "error if maxAwaitTimeMS is equal to timeoutMS" + operations: + - name: find + object: *collection + arguments: + filter: {} + cursorType: tailableAwait + timeoutMS: 5 + maxAwaitTimeMS: 5 + expectError: + isClientError: true + + - description: "timeoutMS applied to find" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + filter: {} + cursorType: tailableAwait + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + tailable: true + awaitData: true + maxTimeMS: { $$exists: true } + + # If maxAwaitTimeMS is not set, timeoutMS should be refreshed for the getMore and the getMore should not have a + # maxTimeMS field. + - description: "timeoutMS is refreshed for getMore if maxAwaitTimeMS is not set" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find", "getMore"] + blockConnection: true + blockTimeMS: 15 + - name: createFindCursor + object: *collection + arguments: + filter: {} + cursorType: tailableAwait + timeoutMS: 29 + batchSize: 1 + saveResultAsEntity: &tailableCursor tailableCursor + # Iterate twice to force a getMore. The first iteration will return the document from the first batch and the + # second will do a getMore. + - name: iterateUntilDocumentOrError + object: *tailableCursor + - name: iterateUntilDocumentOrError + object: *tailableCursor + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + tailable: true + awaitData: true + maxTimeMS: { $$exists: true } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + + # If maxAwaitTimeMS is set for the initial command, timeoutMS should still be refreshed for the getMore and the + # getMore command should have a maxTimeMS field. + - description: "timeoutMS is refreshed for getMore if maxAwaitTimeMS is set" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find", "getMore"] + blockConnection: true + blockTimeMS: 15 + - name: createFindCursor + object: *collection + arguments: + filter: {} + cursorType: tailableAwait + timeoutMS: 29 + batchSize: 1 + maxAwaitTimeMS: 1 + saveResultAsEntity: &tailableCursor tailableCursor + # Iterate twice to force a getMore. + - name: iterateUntilDocumentOrError + object: *tailableCursor + - name: iterateUntilDocumentOrError + object: *tailableCursor + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + tailable: true + awaitData: true + maxTimeMS: { $$exists: true } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: 1 + + # The timeoutMS value should be refreshed for getMore's. This is a failure test. The find inherits timeoutMS=10 from + # the collection and the getMore blocks for 15ms, causing iteration to fail with a timeout error. + - description: "timeoutMS is refreshed for getMore - failure" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + blockTimeMS: 15 + - name: createFindCursor + object: *collection + arguments: + filter: {} + cursorType: tailableAwait + batchSize: 1 + saveResultAsEntity: &tailableCursor tailableCursor + # Iterate twice to force a getMore. + - name: iterateUntilDocumentOrError + object: *tailableCursor + - name: iterateUntilDocumentOrError + object: *tailableCursor + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + tailable: true + awaitData: true + maxTimeMS: { $$exists: true } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName diff --git a/spec/spec_tests/data/client_side_operations_timeout/tailable-non-awaitData.yml b/spec/spec_tests/data/client_side_operations_timeout/tailable-non-awaitData.yml new file mode 100644 index 0000000000..766b46e658 --- /dev/null +++ b/spec/spec_tests/data/client_side_operations_timeout/tailable-non-awaitData.yml @@ -0,0 +1,181 @@ +description: "timeoutMS behaves correctly for tailable non-awaitData cursors" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 10 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + createOptions: + capped: true + size: 500 + documents: + - { _id: 0 } + - { _id: 1 } + +tests: + - description: "error if timeoutMode is cursor_lifetime" + operations: + - name: find + object: *collection + arguments: + filter: {} + timeoutMode: cursorLifetime + cursorType: tailable + expectError: + isClientError: true + + - description: "timeoutMS applied to find" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + filter: {} + cursorType: tailable + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + # Due to SERVER-51153, the find command should not contain a maxTimeMS field for tailable non-awaitData + # cursors because that would cap the lifetime of the created cursor. + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + tailable: true + awaitData: { $$exists: false } + maxTimeMS: { $$exists: false } + + # The timeoutMS option should apply separately to the initial "find" and each getMore. This is a success test. The + # find is executed with timeoutMS=20 and both find and getMore commands are configured to block for 15ms each. Neither + # exceeds the timeout so the operation succeeds. + - description: "timeoutMS is refreshed for getMore - success" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find", "getMore"] + blockConnection: true + blockTimeMS: 15 + - name: createFindCursor + object: *collection + arguments: + filter: {} + cursorType: tailable + timeoutMS: 20 + batchSize: 1 + saveResultAsEntity: &tailableCursor tailableCursor + # Iterate the cursor twice: the first iteration will return the document from the batch in the find and the + # second will do a getMore. + - name: iterateUntilDocumentOrError + object: *tailableCursor + - name: iterateUntilDocumentOrError + object: *tailableCursor + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + tailable: true + awaitData: { $$exists: false } + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + + # The timeoutMS option should apply separately to the initial "find" and each getMore. This is a failure test. The + # find inherits timeoutMS=10 from the collection and the getMore command blocks for 15ms, causing iteration to fail + # with a timeout error. + - description: "timeoutMS is refreshed for getMore - failure" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + blockTimeMS: 15 + - name: createFindCursor + object: *collection + arguments: + filter: {} + cursorType: tailable + batchSize: 1 + saveResultAsEntity: &tailableCursor tailableCursor + # Iterate the cursor twice: the first iteration will return the document from the batch in the find and the + # second will do a getMore. + - name: iterateUntilDocumentOrError + object: *tailableCursor + - name: iterateUntilDocumentOrError + object: *tailableCursor + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + tailable: true + awaitData: { $$exists: false } + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } diff --git a/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml b/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml index 0f22349f1b..611f2ed424 100644 --- a/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml +++ b/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml @@ -8,6 +8,8 @@ runOnRequirements: # https://jira.mongodb.org/browse/DRIVERS-291 maxServerVersion: "7.99" topologies: [ replicaset, sharded, load-balanced ] + # SERVER-90047: failures against latest server necessitate adding this for now + maxServerVersion: "8.0.0" _yamlAnchors: readConcern: &readConcern @@ -61,6 +63,8 @@ tests: - description: "Aggregate with $out includes read preference for 5.0+ server" runOnRequirements: - minServerVersion: "5.0" + # https://jira.mongodb.org/browse/RUBY-3539 + maxServerVersion: "7.99" serverless: "forbid" operations: - object: *collection0 diff --git a/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml b/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml index a58f7f0960..9fa8396381 100644 --- a/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml +++ b/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml @@ -11,6 +11,8 @@ runOnRequirements: maxServerVersion: "7.99" topologies: [ replicaset ] serverless: forbid + # SERVER-90047: failures against latest server necessitate adding this for now + maxServerVersion: "8.0.0" _yamlAnchors: readConcern: &readConcern @@ -54,6 +56,8 @@ tests: - description: "Database-level aggregate with $out includes read preference for 5.0+ server" runOnRequirements: - minServerVersion: "5.0" + # https://jira.mongodb.org/browse/RUBY-3539 + maxServerVersion: "7.99" serverless: "forbid" operations: - object: *database0 diff --git a/spec/spec_tests/data/crud_unified/find-test-all-options.yml b/spec/spec_tests/data/crud_unified/find-test-all-options.yml index 0f456b9cdf..7aebaf504e 100644 --- a/spec/spec_tests/data/crud_unified/find-test-all-options.yml +++ b/spec/spec_tests/data/crud_unified/find-test-all-options.yml @@ -1,3 +1,6 @@ +# This spec is specific to the ruby driver, and is not part of the general +# `specifications` repo. + description: "find options" schemaVersion: "1.0" @@ -162,11 +165,30 @@ tests: commandName: find databaseName: *database0Name + - description: "timeoutMS" + operations: + - name: find + arguments: + filter: *filter + timeoutMS: &timeoutMS 1000 + object: *collection0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: *filter + maxTimeMS: { $$type: [ int ] } + commandName: find + databaseName: *database0Name + - description: "max" operations: - name: find arguments: filter: *filter + hint: { _id: 1 } max: &max { _id: 10 } object: *collection0 expectEvents: @@ -182,6 +204,11 @@ tests: - description: "min" operations: + - name: createIndex + object: *collection0 + arguments: + name: "name_1" + keys: { name: 1 } - name: find arguments: filter: *filter @@ -191,6 +218,8 @@ tests: expectEvents: - client: *client0 events: + - commandStartedEvent: + commandName: createIndexes - commandStartedEvent: command: find: *collection0Name diff --git a/spec/spec_tests/server_selection_rtt_spec.rb b/spec/spec_tests/server_selection_rtt_spec.rb index a12a47e832..ea981846c8 100644 --- a/spec/spec_tests/server_selection_rtt_spec.rb +++ b/spec/spec_tests/server_selection_rtt_spec.rb @@ -15,18 +15,18 @@ context(spec.description) do - let(:averager) do - Mongo::Server::RoundTripTimeAverager.new + let(:calculator) do + Mongo::Server::RoundTripTimeCalculator.new end before do - averager.instance_variable_set(:@average_round_trip_time, spec.average_rtt) - averager.instance_variable_set(:@last_round_trip_time, spec.new_rtt) - averager.send(:update_average_round_trip_time) + calculator.instance_variable_set(:@average_round_trip_time, spec.average_rtt) + calculator.instance_variable_set(:@last_round_trip_time, spec.new_rtt) + calculator.update_average_round_trip_time end it 'correctly calculates the moving average round trip time' do - expect(averager.average_round_trip_time).to eq(spec.new_average_rtt) + expect(calculator.average_round_trip_time).to eq(spec.new_average_rtt) end end end diff --git a/spec/support/certificates/atlas-ocsp-ca.crt b/spec/support/certificates/atlas-ocsp-ca.crt index d4309071e1..1e7edb5447 100644 --- a/spec/support/certificates/atlas-ocsp-ca.crt +++ b/spec/support/certificates/atlas-ocsp-ca.crt @@ -2,35 +2,35 @@ Certificate: Data: Version: 3 (0x2) Serial Number: - 91:2b:08:4a:cf:0c:18:a7:53:f6:d6:2e:25:a7:5f:5a + 4b:a8:52:93:f7:9a:2f:a2:73:06:4b:a8:04:8d:75:d0 Signature Algorithm: sha256WithRSAEncryption Issuer: C=US, O=Internet Security Research Group, CN=ISRG Root X1 Validity - Not Before: Sep 4 00:00:00 2020 GMT - Not After : Sep 15 16:00:00 2025 GMT - Subject: C=US, O=Let's Encrypt, CN=R3 + Not Before: Mar 13 00:00:00 2024 GMT + Not After : Mar 12 23:59:59 2027 GMT + Subject: C=US, O=Let's Encrypt, CN=R10 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: - 00:bb:02:15:28:cc:f6:a0:94:d3:0f:12:ec:8d:55: - 92:c3:f8:82:f1:99:a6:7a:42:88:a7:5d:26:aa:b5: - 2b:b9:c5:4c:b1:af:8e:6b:f9:75:c8:a3:d7:0f:47: - 94:14:55:35:57:8c:9e:a8:a2:39:19:f5:82:3c:42: - a9:4e:6e:f5:3b:c3:2e:db:8d:c0:b0:5c:f3:59:38: - e7:ed:cf:69:f0:5a:0b:1b:be:c0:94:24:25:87:fa: - 37:71:b3:13:e7:1c:ac:e1:9b:ef:db:e4:3b:45:52: - 45:96:a9:c1:53:ce:34:c8:52:ee:b5:ae:ed:8f:de: - 60:70:e2:a5:54:ab:b6:6d:0e:97:a5:40:34:6b:2b: - d3:bc:66:eb:66:34:7c:fa:6b:8b:8f:57:29:99:f8: - 30:17:5d:ba:72:6f:fb:81:c5:ad:d2:86:58:3d:17: - c7:e7:09:bb:f1:2b:f7:86:dc:c1:da:71:5d:d4:46: - e3:cc:ad:25:c1:88:bc:60:67:75:66:b3:f1:18:f7: - a2:5c:e6:53:ff:3a:88:b6:47:a5:ff:13:18:ea:98: - 09:77:3f:9d:53:f9:cf:01:e5:f5:a6:70:17:14:af: - 63:a4:ff:99:b3:93:9d:dc:53:a7:06:fe:48:85:1d: - a1:69:ae:25:75:bb:13:cc:52:03:f5:ed:51:a1:8b: - db:15 + 00:cf:57:e5:e6:c4:54:12:ed:b4:47:fe:c9:27:58: + 76:46:50:28:8c:1d:3e:88:df:05:9d:d5:b5:18:29: + bd:dd:b5:5a:bf:fa:f6:ce:a3:be:af:00:21:4b:62: + 5a:5a:3c:01:2f:c5:58:03:f6:89:ff:8e:11:43:eb: + c1:b5:e0:14:07:96:8f:6f:1f:d7:e7:ba:81:39:09: + 75:65:b7:c2:af:18:5b:37:26:28:e7:a3:f4:07:2b: + 6d:1a:ff:ab:58:bc:95:ae:40:ff:e9:cb:57:c4:b5: + 5b:7f:78:0d:18:61:bc:17:e7:54:c6:bb:49:91:cd: + 6e:18:d1:80:85:ee:a6:65:36:bc:74:ea:bc:50:4c: + ea:fc:21:f3:38:16:93:94:ba:b0:d3:6b:38:06:cd: + 16:12:7a:ca:52:75:c8:ad:76:b2:c2:9c:5d:98:45: + 5c:6f:61:7b:c6:2d:ee:3c:13:52:86:01:d9:57:e6: + 38:1c:df:8d:b5:1f:92:91:9a:e7:4a:1c:cc:45:a8: + 72:55:f0:b0:e6:a3:07:ec:fd:a7:1b:66:9e:3f:48: + 8b:71:84:71:58:c9:3a:fa:ef:5e:f2:5b:44:2b:3c: + 74:e7:8f:b2:47:c1:07:6a:cd:9a:b7:0d:96:f7:12: + 81:26:51:54:0a:ec:61:f6:f7:f5:e2:f2:8a:c8:95: + 0d:8d Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Key Usage: critical @@ -40,76 +40,74 @@ Certificate: X509v3 Basic Constraints: critical CA:TRUE, pathlen:0 X509v3 Subject Key Identifier: - 14:2E:B3:17:B7:58:56:CB:AE:50:09:40:E6:1F:AF:9D:8B:14:C2:C6 + BB:BC:C3:47:A5:E4:BC:A9:C6:C3:A4:72:0C:10:8D:A2:35:E1:C8:E8 X509v3 Authority Key Identifier: 79:B4:59:E6:7B:B6:E5:E4:01:73:80:08:88:C8:1A:58:F6:E9:9B:6E Authority Information Access: CA Issuers - URI:http://x1.i.lencr.org/ + X509v3 Certificate Policies: + Policy: 2.23.140.1.2.1 X509v3 CRL Distribution Points: Full Name: URI:http://x1.c.lencr.org/ - X509v3 Certificate Policies: - Policy: 2.23.140.1.2.1 - Policy: 1.3.6.1.4.1.44947.1.1.1 Signature Algorithm: sha256WithRSAEncryption Signature Value: - 85:ca:4e:47:3e:a3:f7:85:44:85:bc:d5:67:78:b2:98:63:ad: - 75:4d:1e:96:3d:33:65:72:54:2d:81:a0:ea:c3:ed:f8:20:bf: - 5f:cc:b7:70:00:b7:6e:3b:f6:5e:94:de:e4:20:9f:a6:ef:8b: - b2:03:e7:a2:b5:16:3c:91:ce:b4:ed:39:02:e7:7c:25:8a:47: - e6:65:6e:3f:46:f4:d9:f0:ce:94:2b:ee:54:ce:12:bc:8c:27: - 4b:b8:c1:98:2f:a2:af:cd:71:91:4a:08:b7:c8:b8:23:7b:04: - 2d:08:f9:08:57:3e:83:d9:04:33:0a:47:21:78:09:82:27:c3: - 2a:c8:9b:b9:ce:5c:f2:64:c8:c0:be:79:c0:4f:8e:6d:44:0c: - 5e:92:bb:2e:f7:8b:10:e1:e8:1d:44:29:db:59:20:ed:63:b9: - 21:f8:12:26:94:93:57:a0:1d:65:04:c1:0a:22:ae:10:0d:43: - 97:a1:18:1f:7e:e0:e0:86:37:b5:5a:b1:bd:30:bf:87:6e:2b: - 2a:ff:21:4e:1b:05:c3:f5:18:97:f0:5e:ac:c3:a5:b8:6a:f0: - 2e:bc:3b:33:b9:ee:4b:de:cc:fc:e4:af:84:0b:86:3f:c0:55: - 43:36:f6:68:e1:36:17:6a:8e:99:d1:ff:a5:40:a7:34:b7:c0: - d0:63:39:35:39:75:6e:f2:ba:76:c8:93:02:e9:a9:4b:6c:17: - ce:0c:02:d9:bd:81:fb:9f:b7:68:d4:06:65:b3:82:3d:77:53: - f8:8e:79:03:ad:0a:31:07:75:2a:43:d8:55:97:72:c4:29:0e: - f7:c4:5d:4e:c8:ae:46:84:30:d7:f2:85:5f:18:a1:79:bb:e7: - 5e:70:8b:07:e1:86:93:c3:b9:8f:dc:61:71:25:2a:af:df:ed: - 25:50:52:68:8b:92:dc:e5:d6:b5:e3:da:7d:d0:87:6c:84:21: - 31:ae:82:f5:fb:b9:ab:c8:89:17:3d:e1:4c:e5:38:0e:f6:bd: - 2b:bd:96:81:14:eb:d5:db:3d:20:a7:7e:59:d3:e2:f8:58:f9: - 5b:b8:48:cd:fe:5c:4f:16:29:fe:1e:55:23:af:c8:11:b0:8d: - ea:7c:93:90:17:2f:fd:ac:a2:09:47:46:3f:f0:e9:b0:b7:ff: - 28:4d:68:32:d6:67:5e:1e:69:a3:93:b8:f5:9d:8b:2f:0b:d2: - 52:43:a6:6f:32:57:65:4d:32:81:df:38:53:85:5d:7e:5d:66: - 29:ea:b8:dd:e4:95:b5:cd:b5:56:12:42:cd:c4:4e:c6:25:38: - 44:50:6d:ec:ce:00:55:18:fe:e9:49:64:d4:4e:ca:97:9c:b4: - 5b:c0:73:a8:ab:b8:47:c2 + 92:b1:e7:41:37:eb:79:9d:81:e6:cd:e2:25:e1:3a:20:e9:90: + 44:95:a3:81:5c:cf:c3:5d:fd:bd:a0:70:d5:b1:96:28:22:0b: + d2:f2:28:cf:0c:e7:d4:e6:43:8c:24:22:1d:c1:42:92:d1:09: + af:9f:4b:f4:c8:70:4f:20:16:b1:5a:dd:01:f6:1f:f8:1f:61: + 6b:14:27:b0:72:8d:63:ae:ee:e2:ce:4b:cf:37:dd:bb:a3:d4: + cd:e7:ad:50:ad:bd:bf:e3:ec:3e:62:36:70:99:31:a7:e8:8d: + dd:ea:62:e2:12:ae:f5:9c:d4:3d:2c:0c:aa:d0:9c:79:be:ea: + 3d:5c:44:6e:96:31:63:5a:7d:d6:7e:4f:24:a0:4b:05:7f:5e: + 6f:d2:d4:ea:5f:33:4b:13:d6:57:b6:ca:de:51:b8:5d:a3:09: + 82:74:fd:c7:78:9e:b3:b9:ac:16:da:4a:2b:96:c3:b6:8b:62: + 8f:f9:74:19:a2:9e:03:de:e9:6f:9b:b0:0f:d2:a0:5a:f6:85: + 5c:c2:04:b7:c8:d5:4e:32:c4:bf:04:5d:bc:29:f6:f7:81:8f: + 0c:5d:3c:53:c9:40:90:8b:fb:b6:08:65:b9:a4:21:d5:09:e5: + 13:84:84:37:82:ce:10:28:fc:76:c2:06:25:7a:46:52:4d:da: + 53:72:a4:27:3f:62:70:ac:be:69:48:00:fb:67:0f:db:5b:a1: + e8:d7:03:21:2d:d7:c9:f6:99:42:39:83:43:df:77:0a:12:08: + f1:25:d6:ba:94:19:54:18:88:a5:c5:8e:e1:1a:99:93:79:6b: + ec:1c:f9:31:40:b0:cc:32:00:df:9f:5e:e7:b4:92:ab:90:82: + 91:8d:0d:e0:1e:95:ba:59:3b:2e:4b:5f:c2:b7:46:35:52:39: + 06:c0:bd:aa:ac:52:c1:22:a0:44:97:99:f7:0c:a0:21:a7:a1: + 6c:71:47:16:17:01:68:c0:ca:a6:26:65:04:7c:b3:ae:c9:e7: + 94:55:c2:6f:9b:3c:1c:a9:f9:2e:c5:20:1a:f0:76:e0:be:ec: + 18:d6:4f:d8:25:fb:76:11:e8:bf:e6:21:0f:e8:e8:cc:b5:b6: + a7:d5:b8:f7:9f:41:cf:61:22:46:6a:83:b6:68:97:2e:7c:ea: + 4e:95:db:23:eb:2e:c8:2b:28:84:a4:60:e9:49:f4:44:2e:3b: + f9:ca:62:57:01:e2:5d:90:16:f9:c9:fc:7a:23:48:8e:a6:d5: + 81:72:f1:28:fa:5d:ce:fb:ed:4e:73:8f:94:2e:d2:41:94:98: + 99:db:a7:af:70:5f:f5:be:fb:02:20:bf:66:27:6c:b4:ad:fa: + 75:12:0b:2b:3e:ce:03:9e -----BEGIN CERTIFICATE----- -MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw -TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh -cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw -WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg -RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP -R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx -sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm -NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg -Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG -/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC -AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB -Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA -FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw -AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw -Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB -gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W -PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl -ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz -CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm -lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4 -avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2 -yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O -yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids -hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+ -HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv -MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX -nLRbwHOoq7hHwg== +MIIFBTCCAu2gAwIBAgIQS6hSk/eaL6JzBkuoBI110DANBgkqhkiG9w0BAQsFADBP +MQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFy +Y2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTAeFw0yNDAzMTMwMDAwMDBa +Fw0yNzAzMTIyMzU5NTlaMDMxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBF +bmNyeXB0MQwwCgYDVQQDEwNSMTAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDPV+XmxFQS7bRH/sknWHZGUCiMHT6I3wWd1bUYKb3dtVq/+vbOo76vACFL +YlpaPAEvxVgD9on/jhFD68G14BQHlo9vH9fnuoE5CXVlt8KvGFs3Jijno/QHK20a +/6tYvJWuQP/py1fEtVt/eA0YYbwX51TGu0mRzW4Y0YCF7qZlNrx06rxQTOr8IfM4 +FpOUurDTazgGzRYSespSdcitdrLCnF2YRVxvYXvGLe48E1KGAdlX5jgc3421H5KR +mudKHMxFqHJV8LDmowfs/acbZp4/SItxhHFYyTr6717yW0QrPHTnj7JHwQdqzZq3 +DZb3EoEmUVQK7GH29/Xi8orIlQ2NAgMBAAGjgfgwgfUwDgYDVR0PAQH/BAQDAgGG +MB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATASBgNVHRMBAf8ECDAGAQH/ +AgEAMB0GA1UdDgQWBBS7vMNHpeS8qcbDpHIMEI2iNeHI6DAfBgNVHSMEGDAWgBR5 +tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAKG +Fmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0gBAwwCjAIBgZngQwBAgEwJwYD +VR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVuY3Iub3JnLzANBgkqhkiG9w0B +AQsFAAOCAgEAkrHnQTfreZ2B5s3iJeE6IOmQRJWjgVzPw139vaBw1bGWKCIL0vIo +zwzn1OZDjCQiHcFCktEJr59L9MhwTyAWsVrdAfYf+B9haxQnsHKNY67u4s5Lzzfd +u6PUzeetUK29v+PsPmI2cJkxp+iN3epi4hKu9ZzUPSwMqtCceb7qPVxEbpYxY1p9 +1n5PJKBLBX9eb9LU6l8zSxPWV7bK3lG4XaMJgnT9x3ies7msFtpKK5bDtotij/l0 +GaKeA97pb5uwD9KgWvaFXMIEt8jVTjLEvwRdvCn294GPDF08U8lAkIv7tghluaQh +1QnlE4SEN4LOECj8dsIGJXpGUk3aU3KkJz9icKy+aUgA+2cP21uh6NcDIS3XyfaZ +QjmDQ993ChII8SXWupQZVBiIpcWO4RqZk3lr7Bz5MUCwzDIA359e57SSq5CCkY0N +4B6Vulk7LktfwrdGNVI5BsC9qqxSwSKgRJeZ9wygIaehbHFHFhcBaMDKpiZlBHyz +rsnnlFXCb5s8HKn5LsUgGvB24L7sGNZP2CX7dhHov+YhD+jozLW2p9W4959Bz2Ei +RmqDtmiXLnzqTpXbI+suyCsohKRg6Un0RC47+cpiVwHiXZAW+cn8eiNIjqbVgXLx +KPpdzvvtTnOPlC7SQZSYmdunr3Bf9b77AiC/ZidstK36dRILKz7OA54= -----END CERTIFICATE----- \ No newline at end of file diff --git a/spec/support/certificates/atlas-ocsp.crt b/spec/support/certificates/atlas-ocsp.crt index ad4d13a368..9a486282bb 100644 --- a/spec/support/certificates/atlas-ocsp.crt +++ b/spec/support/certificates/atlas-ocsp.crt @@ -2,52 +2,52 @@ Certificate: Data: Version: 3 (0x2) Serial Number: - 04:ae:c2:45:fc:ba:ae:16:d6:38:7e:de:04:c4:2b:41:fd:a0 + 03:92:42:45:e6:7a:a2:13:84:ea:7c:7e:ce:da:6e:d4:63:67 Signature Algorithm: sha256WithRSAEncryption - Issuer: C=US, O=Let's Encrypt, CN=R3 + Issuer: C=US, O=Let's Encrypt, CN=R10 Validity - Not Before: Jun 5 14:02:45 2024 GMT - Not After : Sep 3 14:02:44 2024 GMT + Not Before: Aug 20 13:05:04 2024 GMT + Not After : Nov 18 13:05:03 2024 GMT Subject: CN=*.g6fyiaq.mongodb-dev.net Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (4096 bit) Modulus: - 00:c0:d4:93:f2:7c:b5:64:56:a2:ed:07:ab:96:00: - 61:8d:74:03:ee:10:dc:7f:92:26:ea:b1:c9:2f:7a: - 33:d2:05:b2:25:ff:15:c0:fc:37:5c:8c:a9:36:82: - 66:eb:07:70:f9:ec:b3:ca:88:9d:07:51:ef:f2:18: - e3:88:3b:f7:f5:b2:29:e2:7f:7b:00:dc:9f:b8:95: - cf:f4:58:37:0d:cd:b0:ec:9b:d0:c3:ec:71:d8:4d: - 97:5a:a2:c6:66:2e:c1:dc:4f:38:f3:ba:43:3d:3e: - 46:12:a8:08:7a:e3:bc:fa:52:3f:b4:4f:3e:25:9f: - 4a:c9:fd:af:42:cc:62:59:ba:e1:92:1c:6c:57:2d: - 86:fb:62:08:08:5b:f9:5e:3c:3b:e8:9a:b2:1c:a0: - 82:58:0c:e9:40:c2:21:c2:71:93:32:68:44:f9:61: - 13:bc:d6:c8:71:1e:9c:37:f4:b9:cd:13:30:cd:4b: - 42:c0:c5:95:91:86:b5:e9:7f:f6:37:de:20:8e:cc: - 05:3d:14:aa:29:c2:bf:4f:34:71:89:07:54:5b:b4: - cd:13:bd:53:74:e7:6f:33:c5:73:36:24:5b:37:88: - d1:9e:fc:98:e1:7a:a2:25:65:c5:b1:21:ad:2c:99: - 0b:03:2e:92:06:ef:c1:31:b6:3b:00:32:00:f5:c4: - 70:0d:1d:43:1c:a7:31:03:40:a6:95:6c:29:b3:93: - 54:3b:9f:bd:5f:44:be:ba:04:a9:5c:f1:2d:0d:04: - 18:51:27:75:47:d7:4b:06:e7:df:bc:2c:4e:c7:4b: - 18:60:de:51:ce:41:a9:6c:6a:19:d1:a5:0b:03:e5: - ec:89:10:6b:b4:94:ba:79:b6:06:11:2e:b2:5f:e6: - 19:f0:f9:ad:3e:4f:a4:cc:3d:7b:69:46:ed:71:1c: - f6:50:6c:53:08:9e:a3:05:59:de:fb:36:a1:92:ad: - 7d:78:c6:37:13:d1:1a:9a:56:cc:fb:bd:21:34:ec: - 8d:a5:73:59:6f:4e:aa:e2:2e:16:d6:d6:89:92:a3: - 8f:d7:b0:46:cf:07:70:75:dc:dc:b3:70:cf:2a:12: - 84:4e:41:57:10:68:7b:5f:f9:8f:64:d2:d4:e9:c0: - eb:b4:68:37:87:40:32:9e:f6:7f:be:31:fb:d5:36: - a4:15:ba:52:24:c1:5d:ae:d0:41:e3:ff:88:09:7d: - e7:98:23:bb:09:9b:7b:6d:e4:c0:ca:7a:1b:6e:32: - 63:69:42:7b:26:9d:c9:f0:c3:b9:58:88:c3:74:9d: - d3:54:3b:20:09:76:5c:54:4e:60:f7:35:53:e4:f9: - aa:af:0e:bd:24:d1:0e:c5:8a:7e:26:f0:e7:a0:26: - d0:9d:01 + 00:c2:ff:e0:09:f7:d1:30:47:e6:a1:c1:92:c2:ba: + cf:de:2c:86:2e:34:3a:63:f4:5a:a9:45:25:89:47: + eb:44:e8:fb:4e:0d:ac:99:5d:f8:42:74:07:a5:95: + ba:81:8b:9f:f1:64:f0:37:4e:e7:f6:59:c3:3a:08: + 5b:82:55:cd:ea:81:94:b6:e7:ec:7a:d8:6b:09:41: + 40:40:2f:27:b4:0e:09:d8:61:82:dc:b3:43:5b:50: + 5d:03:78:08:ed:35:52:e6:3a:9b:ad:de:4f:73:b3: + 56:2f:25:0d:e7:0b:61:83:e8:35:fe:73:89:e3:0e: + cb:7d:c1:29:00:d4:e2:a8:c2:9c:5c:b0:fd:4d:be: + 1a:86:a7:7f:b3:d3:d9:3b:35:4c:82:b3:8d:55:0a: + 9b:77:40:b0:dd:b1:e6:91:f4:91:dc:50:ee:96:92: + cc:af:11:bb:43:0a:a4:2b:5a:00:ab:9c:17:a2:e1: + bf:7c:9e:92:04:01:8f:bf:16:5f:85:9c:e4:5c:37: + 97:ff:29:50:18:d7:01:66:c5:bc:51:11:ac:8a:a3: + de:58:5d:1c:44:e4:f4:fe:77:83:99:3f:57:71:2d: + 2e:95:07:cc:78:b8:3c:50:4b:ea:ca:a8:20:e8:9b: + 05:91:5c:40:ba:a9:c3:87:d5:95:d7:ab:67:33:03: + 1d:b9:c6:d7:ef:3a:4e:aa:7d:81:bc:1b:52:fb:0e: + b4:7c:ce:9e:ff:d8:08:2b:33:ee:b6:d3:5c:7c:1a: + 7e:a3:cf:cd:92:42:7f:1b:a4:36:0b:d9:51:39:c6: + cb:c1:65:c1:e2:84:53:30:ba:2e:f1:c3:07:71:09: + 69:dd:ff:92:16:2c:05:1d:60:28:1c:af:5e:76:88: + 9f:df:e7:97:fb:cd:19:48:7a:87:f6:24:e0:e1:e1: + ff:76:95:93:65:72:44:29:5e:69:5d:2d:26:2c:fb: + a7:06:63:ff:7f:02:29:82:61:42:d9:9a:0b:44:ea: + 89:c8:bc:4a:75:17:58:05:85:04:62:1f:70:bd:79: + 66:b6:bb:27:a6:88:c8:27:db:41:da:88:ec:4e:71: + 0a:20:e6:e3:79:2a:ee:b5:af:99:96:72:9d:ca:c3: + e7:4f:9d:cd:e4:6b:22:e4:3b:54:2f:e2:e8:0c:df: + 6f:14:f8:74:4c:21:15:28:2c:51:5e:c8:8c:86:8e: + e0:5e:0e:2d:e1:25:cc:47:8c:9e:b5:94:bb:34:e4: + 43:b1:cd:55:2a:6f:1f:14:fa:c2:2f:3c:a1:ba:65: + f5:09:8d:1c:20:12:0d:80:33:35:f7:2f:d1:8b:ca: + b8:77:f0:a3:7d:fa:bd:31:ba:3a:f4:c7:5e:8d:55: + a7:c9:69 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Key Usage: critical @@ -57,12 +57,12 @@ Certificate: X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Key Identifier: - 03:4A:67:8B:0B:1C:86:4C:9D:D9:E6:E2:43:B1:BA:93:53:50:DE:28 + B8:6F:A5:A0:D0:70:D7:5E:AE:1E:54:5A:F1:26:DA:DD:CB:2C:5E:6F X509v3 Authority Key Identifier: - 14:2E:B3:17:B7:58:56:CB:AE:50:09:40:E6:1F:AF:9D:8B:14:C2:C6 + BB:BC:C3:47:A5:E4:BC:A9:C6:C3:A4:72:0C:10:8D:A2:35:E1:C8:E8 Authority Information Access: - OCSP - URI:http://r3.o.lencr.org - CA Issuers - URI:http://r3.i.lencr.org/ + OCSP - URI:http://r10.o.lencr.org + CA Issuers - URI:http://r10.i.lencr.org/ X509v3 Subject Alternative Name: DNS:*.g6fyiaq.mesh.mongodb-dev.net, DNS:*.g6fyiaq.mongodb-dev.net X509v3 Certificate Policies: @@ -70,78 +70,78 @@ Certificate: CT Precertificate SCTs: Signed Certificate Timestamp: Version : v1 (0x0) - Log ID : 3F:17:4B:4F:D7:22:47:58:94:1D:65:1C:84:BE:0D:12: - ED:90:37:7F:1F:85:6A:EB:C1:BF:28:85:EC:F8:64:6E - Timestamp : Jun 5 15:02:46.375 2024 GMT + Log ID : 76:FF:88:3F:0A:B6:FB:95:51:C2:61:CC:F5:87:BA:34: + B4:A4:CD:BB:29:DC:68:42:0A:9F:E6:67:4C:5A:3A:74 + Timestamp : Aug 20 14:03:34.519 2024 GMT Extensions: none Signature : ecdsa-with-SHA256 - 30:45:02:21:00:A2:21:6F:59:A8:6F:DF:1F:D8:BF:D5: - 0C:C8:90:9C:15:F6:02:20:97:22:70:BD:DF:C6:F8:41: - 02:A3:86:FB:0F:02:20:5F:4C:E5:7D:38:D7:AF:7C:40: - 41:B1:99:8A:04:83:2C:95:7F:71:34:42:2F:B9:94:DC: - EE:85:D9:2E:6A:FE:0E + 30:46:02:21:00:C6:92:47:D8:40:3A:B9:0C:BD:FC:45: + 3E:C6:28:06:80:62:0F:4A:EF:44:56:5F:8C:16:DF:C6: + A4:0E:4C:76:DD:02:21:00:CA:36:0F:54:6B:84:37:F3: + AD:50:5B:E8:DD:3D:D2:EA:34:82:72:50:D2:2C:2A:47: + FB:EC:16:76:5A:E8:9E:0C Signed Certificate Timestamp: Version : v1 (0x0) - Log ID : 76:FF:88:3F:0A:B6:FB:95:51:C2:61:CC:F5:87:BA:34: - B4:A4:CD:BB:29:DC:68:42:0A:9F:E6:67:4C:5A:3A:74 - Timestamp : Jun 5 15:02:46.419 2024 GMT + Log ID : DF:E1:56:EB:AA:05:AF:B5:9C:0F:86:71:8D:A8:C0:32: + 4E:AE:56:D9:6E:A7:F5:A5:6A:01:D1:C1:3B:BE:52:5C + Timestamp : Aug 20 14:03:34.658 2024 GMT Extensions: none Signature : ecdsa-with-SHA256 - 30:46:02:21:00:FA:63:32:6E:58:54:6F:36:EE:9C:BA: - FB:AF:E1:5E:B5:A9:24:8A:81:84:7A:E5:F3:A9:38:42: - 57:CC:E3:09:C9:02:21:00:E2:BD:7E:3D:45:28:05:91: - F6:46:1C:EA:5C:31:DA:4A:88:D2:F8:A2:98:9E:E3:A1: - 36:1D:41:BF:DA:0A:45:D9 + 30:45:02:20:23:5B:E1:35:10:78:F9:99:9F:42:7F:FE: + 73:F4:74:1B:55:3D:B9:93:C9:A8:EE:E7:B2:61:52:12: + D1:C9:06:0C:02:21:00:87:EA:87:A3:3A:B2:C6:F0:EA: + 52:5A:B2:7F:02:2E:CF:68:C8:A5:CB:54:0F:CB:CE:6A: + CC:E0:3A:D1:09:D3:9C Signature Algorithm: sha256WithRSAEncryption Signature Value: - 61:76:67:a7:27:1f:ac:52:59:f6:73:dd:98:6b:52:25:b2:a3: - 1b:f1:23:8f:07:75:7f:e9:1a:fa:c7:99:61:3c:c4:d6:d4:24: - a6:2e:e9:67:c6:18:ca:76:57:63:bf:ce:ad:74:92:e1:e5:84: - 1d:94:80:f2:a9:90:a5:8e:83:74:80:cf:d1:2e:be:f7:13:6e: - 84:22:44:e1:a9:bd:60:85:81:ad:ee:17:68:d1:e7:83:dd:aa: - 8e:35:5e:0b:e5:9a:52:23:4d:c6:85:cc:fc:45:09:d3:f7:63: - 1d:72:69:09:99:36:53:97:43:54:53:2e:47:8d:ec:29:f1:d2: - 54:e4:35:f9:84:a1:ef:f0:4b:23:cf:1c:b3:06:c5:6a:bc:d9: - 7e:b3:8c:3a:76:74:28:d8:2b:75:b2:fd:bb:c3:62:02:46:ed: - ca:47:b2:c9:7a:f3:99:e3:c8:5c:6a:c0:02:a4:25:b9:2d:82: - 87:6c:7a:1f:5d:41:37:53:8f:8a:71:91:13:ed:d7:3f:ac:29: - 58:83:08:1f:1d:62:d4:e9:d9:c8:63:05:0d:24:44:45:44:58: - be:4a:09:ff:e5:56:e8:67:ad:2d:ee:42:f9:a3:fa:fa:3f:72: - ab:eb:eb:4b:23:94:f3:ac:91:f5:34:6a:8a:3d:2f:89:52:54: - a4:7e:5f:db + 1d:11:b5:c6:7e:71:6c:63:34:8e:d4:eb:e6:42:ca:ce:fd:0d: + 9a:7d:ea:49:43:8d:de:46:ad:27:09:a6:a7:5c:58:89:2f:47: + 03:68:e2:19:0f:f6:76:be:47:0a:b6:d1:ed:5d:71:13:2a:12: + de:5e:41:cf:e1:a3:2c:46:07:81:da:b1:86:66:61:b0:0b:70: + 19:33:4c:a3:29:e4:e6:79:f6:3f:1b:4a:51:6a:2a:0c:c1:07: + 2c:db:cc:9a:3f:17:a2:ff:ac:19:76:9e:a2:d4:9b:c9:c2:75: + 48:5c:fd:d4:5e:ff:cc:6b:f0:ea:73:da:0b:f8:fd:c5:92:42: + ca:ca:43:51:98:e5:4b:77:b6:0f:da:d2:83:33:77:bc:60:5c: + b7:60:12:42:10:78:5b:ed:cd:83:42:63:ba:96:de:0e:d8:9e: + a5:97:6a:6f:70:82:7c:82:2c:ca:e3:a3:34:61:7a:70:d1:03: + fc:89:06:1e:b4:f3:ed:b4:64:5f:54:b8:d5:6e:31:e0:fa:0b: + f6:be:b7:6c:38:78:f8:bb:22:f2:7c:6b:44:54:3e:91:3a:8c: + bd:4d:1b:b5:8a:a6:df:17:9b:cf:3a:bd:dc:c3:1e:c5:2c:f5: + 19:32:75:0f:7b:54:30:ab:bb:7e:db:43:fb:ed:16:d9:03:81: + 23:8a:8c:7a -----BEGIN CERTIFICATE----- -MIIGIDCCBQigAwIBAgISBK7CRfy6rhbWOH7eBMQrQf2gMA0GCSqGSIb3DQEBCwUA -MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD -EwJSMzAeFw0yNDA2MDUxNDAyNDVaFw0yNDA5MDMxNDAyNDRaMCQxIjAgBgNVBAMM -GSouZzZmeWlhcS5tb25nb2RiLWRldi5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4IC -DwAwggIKAoICAQDA1JPyfLVkVqLtB6uWAGGNdAPuENx/kibqsckvejPSBbIl/xXA -/DdcjKk2gmbrB3D57LPKiJ0HUe/yGOOIO/f1sinif3sA3J+4lc/0WDcNzbDsm9DD -7HHYTZdaosZmLsHcTzjzukM9PkYSqAh647z6Uj+0Tz4ln0rJ/a9CzGJZuuGSHGxX -LYb7YggIW/lePDvomrIcoIJYDOlAwiHCcZMyaET5YRO81shxHpw39LnNEzDNS0LA -xZWRhrXpf/Y33iCOzAU9FKopwr9PNHGJB1RbtM0TvVN0528zxXM2JFs3iNGe/Jjh -eqIlZcWxIa0smQsDLpIG78ExtjsAMgD1xHANHUMcpzEDQKaVbCmzk1Q7n71fRL66 -BKlc8S0NBBhRJ3VH10sG59+8LE7HSxhg3lHOQalsahnRpQsD5eyJEGu0lLp5tgYR -LrJf5hnw+a0+T6TMPXtpRu1xHPZQbFMInqMFWd77NqGSrX14xjcT0RqaVsz7vSE0 -7I2lc1lvTqriLhbW1omSo4/XsEbPB3B13NyzcM8qEoROQVcQaHtf+Y9k0tTpwOu0 -aDeHQDKe9n++MfvVNqQVulIkwV2u0EHj/4gJfeeYI7sJm3tt5MDKehtuMmNpQnsm -ncnww7lYiMN0ndNUOyAJdlxUTmD3NVPk+aqvDr0k0Q7Fin4m8OegJtCdAQIDAQAB -o4ICPDCCAjgwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr -BgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQDSmeLCxyGTJ3Z5uJDsbqT -U1DeKDAfBgNVHSMEGDAWgBQULrMXt1hWy65QCUDmH6+dixTCxjBVBggrBgEFBQcB -AQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6Ly9yMy5vLmxlbmNyLm9yZzAiBggrBgEF -BQcwAoYWaHR0cDovL3IzLmkubGVuY3Iub3JnLzBEBgNVHREEPTA7gh4qLmc2Znlp -YXEubWVzaC5tb25nb2RiLWRldi5uZXSCGSouZzZmeWlhcS5tb25nb2RiLWRldi5u -ZXQwEwYDVR0gBAwwCjAIBgZngQwBAgEwggEFBgorBgEEAdZ5AgQCBIH2BIHzAPEA -dgA/F0tP1yJHWJQdZRyEvg0S7ZA3fx+FauvBvyiF7PhkbgAAAY/o7IdnAAAEAwBH -MEUCIQCiIW9ZqG/fH9i/1QzIkJwV9gIglyJwvd/G+EECo4b7DwIgX0zlfTjXr3xA -QbGZigSDLJV/cTRCL7mU3O6F2S5q/g4AdwB2/4g/Crb7lVHCYcz1h7o0tKTNuync -aEIKn+ZnTFo6dAAAAY/o7IeTAAAEAwBIMEYCIQD6YzJuWFRvNu6cuvuv4V61qSSK -gYR65fOpOEJXzOMJyQIhAOK9fj1FKAWR9kYc6lwx2kqI0viimJ7joTYdQb/aCkXZ -MA0GCSqGSIb3DQEBCwUAA4IBAQBhdmenJx+sUln2c92Ya1IlsqMb8SOPB3V/6Rr6 -x5lhPMTW1CSmLulnxhjKdldjv86tdJLh5YQdlIDyqZCljoN0gM/RLr73E26EIkTh -qb1ghYGt7hdo0eeD3aqONV4L5ZpSI03Ghcz8RQnT92MdcmkJmTZTl0NUUy5Hjewp -8dJU5DX5hKHv8EsjzxyzBsVqvNl+s4w6dnQo2Ct1sv27w2ICRu3KR7LJevOZ48hc -asACpCW5LYKHbHofXUE3U4+KcZET7dc/rClYgwgfHWLU6dnIYwUNJERFRFi+Sgn/ -5VboZ60t7kL5o/r6P3Kr6+tLI5TzrJH1NGqKPS+JUlSkfl/b +MIIGIzCCBQugAwIBAgISA5JCReZ6ohOE6nx+ztpu1GNnMA0GCSqGSIb3DQEBCwUA +MDMxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQD +EwNSMTAwHhcNMjQwODIwMTMwNTA0WhcNMjQxMTE4MTMwNTAzWjAkMSIwIAYDVQQD +DBkqLmc2ZnlpYXEubW9uZ29kYi1kZXYubmV0MIICIjANBgkqhkiG9w0BAQEFAAOC +Ag8AMIICCgKCAgEAwv/gCffRMEfmocGSwrrP3iyGLjQ6Y/RaqUUliUfrROj7Tg2s +mV34QnQHpZW6gYuf8WTwN07n9lnDOghbglXN6oGUtufsethrCUFAQC8ntA4J2GGC +3LNDW1BdA3gI7TVS5jqbrd5Pc7NWLyUN5wthg+g1/nOJ4w7LfcEpANTiqMKcXLD9 +Tb4ahqd/s9PZOzVMgrONVQqbd0Cw3bHmkfSR3FDulpLMrxG7QwqkK1oAq5wXouG/ +fJ6SBAGPvxZfhZzkXDeX/ylQGNcBZsW8URGsiqPeWF0cROT0/neDmT9XcS0ulQfM +eLg8UEvqyqgg6JsFkVxAuqnDh9WV16tnMwMducbX7zpOqn2BvBtS+w60fM6e/9gI +KzPuttNcfBp+o8/NkkJ/G6Q2C9lROcbLwWXB4oRTMLou8cMHcQlp3f+SFiwFHWAo +HK9edoif3+eX+80ZSHqH9iTg4eH/dpWTZXJEKV5pXS0mLPunBmP/fwIpgmFC2ZoL +ROqJyLxKdRdYBYUEYh9wvXlmtrsnpojIJ9tB2ojsTnEKIObjeSruta+ZlnKdysPn +T53N5Gsi5DtUL+LoDN9vFPh0TCEVKCxRXsiMho7gXg4t4SXMR4yetZS7NORDsc1V +Km8fFPrCLzyhumX1CY0cIBINgDM19y/Ri8q4d/Cjffq9Mbo69MdejVWnyWkCAwEA +AaOCAj4wggI6MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYI +KwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUuG+loNBw116uHlRa8Sba +3cssXm8wHwYDVR0jBBgwFoAUu7zDR6XkvKnGw6RyDBCNojXhyOgwVwYIKwYBBQUH +AQEESzBJMCIGCCsGAQUFBzABhhZodHRwOi8vcjEwLm8ubGVuY3Iub3JnMCMGCCsG +AQUFBzAChhdodHRwOi8vcjEwLmkubGVuY3Iub3JnLzBEBgNVHREEPTA7gh4qLmc2 +ZnlpYXEubWVzaC5tb25nb2RiLWRldi5uZXSCGSouZzZmeWlhcS5tb25nb2RiLWRl +di5uZXQwEwYDVR0gBAwwCjAIBgZngQwBAgEwggEFBgorBgEEAdZ5AgQCBIH2BIHz +APEAdwB2/4g/Crb7lVHCYcz1h7o0tKTNuyncaEIKn+ZnTFo6dAAAAZFwGaT3AAAE +AwBIMEYCIQDGkkfYQDq5DL38RT7GKAaAYg9K70RWX4wW38akDkx23QIhAMo2D1Rr +hDfzrVBb6N090uo0gnJQ0iwqR/vsFnZa6J4MAHYA3+FW66oFr7WcD4ZxjajAMk6u +Vtlup/WlagHRwTu+UlwAAAGRcBmlggAABAMARzBFAiAjW+E1EHj5mZ9Cf/5z9HQb +VT25k8mo7ueyYVIS0ckGDAIhAIfqh6M6ssbw6lJasn8CLs9oyKXLVA/LzmrM4DrR +CdOcMA0GCSqGSIb3DQEBCwUAA4IBAQAdEbXGfnFsYzSO1OvmQsrO/Q2afepJQ43e +Rq0nCaanXFiJL0cDaOIZD/Z2vkcKttHtXXETKhLeXkHP4aMsRgeB2rGGZmGwC3AZ +M0yjKeTmefY/G0pRaioMwQcs28yaPxei/6wZdp6i1JvJwnVIXP3UXv/Ma/Dqc9oL ++P3FkkLKykNRmOVLd7YP2tKDM3e8YFy3YBJCEHhb7c2DQmO6lt4O2J6ll2pvcIJ8 +gizK46M0YXpw0QP8iQYetPPttGRfVLjVbjHg+gv2vrdsOHj4uyLyfGtEVD6ROoy9 +TRu1iqbfF5vPOr3cwx7FLPUZMnUPe1Qwq7t+20P77RbZA4Ejiox6 -----END CERTIFICATE----- \ No newline at end of file diff --git a/spec/support/cluster_tools.rb b/spec/support/cluster_tools.rb index 253782da98..3eeeef2beb 100644 --- a/spec/support/cluster_tools.rb +++ b/spec/support/cluster_tools.rb @@ -98,7 +98,7 @@ def reset_priorities def step_down admin_client.database.command( replSetStepDown: 4, secondaryCatchUpPeriodSecs: 2) - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e # While waiting for secondaries to catch up before stepping down, this node decided to step down for other reasons (189) if e.code == 189 # success @@ -118,7 +118,7 @@ def step_up(address) begin client.database.command(replSetStepUp: 1) break - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e # Election failed. (125) if e.code == 125 # Possible reason is the node we are trying to elect has deny-listed @@ -261,7 +261,7 @@ def encourage_primary(address) def unfreeze_server(address) begin direct_client(address).use('admin').database.command(replSetFreeze: 0) - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e # Mongo::Error::OperationFailure: cannot freeze node when primary or running for election. state: Primary (95) if e.code == 95 # The server we want to become primary may have already become the diff --git a/spec/support/common_shortcuts.rb b/spec/support/common_shortcuts.rb index 32d386b36a..8eacdf2c7c 100644 --- a/spec/support/common_shortcuts.rb +++ b/spec/support/common_shortcuts.rb @@ -176,7 +176,7 @@ def kill_all_server_sessions ClientRegistry.instance.global_client('root_authorized').command(killAllSessions: []) # killAllSessions also kills the implicit session which the driver uses # to send this command, as a result it always fails - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e # "operation was interrupted" unless e.code == 11601 raise @@ -396,7 +396,7 @@ def wait_for_snapshot(db: nil, collection: nil, client: nil) client.start_session(snapshot: true) do |session| client[collection].aggregate([{'$match': {any: true}}], session: session).to_a end - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e # Retry them as the server demands... if e.code == 246 # SnapshotUnavailable if Mongo::Utils.monotonic_time < start_time + 10 diff --git a/spec/support/crypt/encrypted_fields/range-encryptedFields-Date.json b/spec/support/crypt/encrypted_fields/range-encryptedFields-Date.json index e19fc1e182..2966548ef9 100644 --- a/spec/support/crypt/encrypted_fields/range-encryptedFields-Date.json +++ b/spec/support/crypt/encrypted_fields/range-encryptedFields-Date.json @@ -10,7 +10,7 @@ "path": "encryptedDate", "bsonType": "date", "queries": { - "queryType": "rangePreview", + "queryType": "range", "sparsity": { "$numberLong": "1" }, diff --git a/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalNoPrecision.json b/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalNoPrecision.json index c6d129d4ca..a061165aeb 100644 --- a/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalNoPrecision.json +++ b/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalNoPrecision.json @@ -10,7 +10,7 @@ "path": "encryptedDecimalNoPrecision", "bsonType": "decimal", "queries": { - "queryType": "rangePreview", + "queryType": "range", "sparsity": { "$numberInt": "1" } diff --git a/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalPrecision.json b/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalPrecision.json index c23c3fa923..6081b3d740 100644 --- a/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalPrecision.json +++ b/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalPrecision.json @@ -10,7 +10,7 @@ "path": "encryptedDecimalPrecision", "bsonType": "decimal", "queries": { - "queryType": "rangePreview", + "queryType": "range", "sparsity": { "$numberInt": "1" }, diff --git a/spec/support/crypt/encrypted_fields/range-encryptedFields-DoubleNoPrecision.json b/spec/support/crypt/encrypted_fields/range-encryptedFields-DoubleNoPrecision.json index 4af6422714..b922e1c11b 100644 --- a/spec/support/crypt/encrypted_fields/range-encryptedFields-DoubleNoPrecision.json +++ b/spec/support/crypt/encrypted_fields/range-encryptedFields-DoubleNoPrecision.json @@ -10,7 +10,7 @@ "path": "encryptedDoubleNoPrecision", "bsonType": "double", "queries": { - "queryType": "rangePreview", + "queryType": "range", "sparsity": { "$numberLong": "1" } diff --git a/spec/support/crypt/encrypted_fields/range-encryptedFields-DoublePrecision.json b/spec/support/crypt/encrypted_fields/range-encryptedFields-DoublePrecision.json index c1f388219d..fbd984b7eb 100644 --- a/spec/support/crypt/encrypted_fields/range-encryptedFields-DoublePrecision.json +++ b/spec/support/crypt/encrypted_fields/range-encryptedFields-DoublePrecision.json @@ -10,7 +10,7 @@ "path": "encryptedDoublePrecision", "bsonType": "double", "queries": { - "queryType": "rangePreview", + "queryType": "range", "sparsity": { "$numberLong": "1" }, diff --git a/spec/support/crypt/encrypted_fields/range-encryptedFields-Int.json b/spec/support/crypt/encrypted_fields/range-encryptedFields-Int.json index 217bf6743c..a5ba8e93a7 100644 --- a/spec/support/crypt/encrypted_fields/range-encryptedFields-Int.json +++ b/spec/support/crypt/encrypted_fields/range-encryptedFields-Int.json @@ -10,7 +10,7 @@ "path": "encryptedInt", "bsonType": "int", "queries": { - "queryType": "rangePreview", + "queryType": "range", "sparsity": { "$numberLong": "1" }, diff --git a/spec/support/crypt/encrypted_fields/range-encryptedFields-Long.json b/spec/support/crypt/encrypted_fields/range-encryptedFields-Long.json index 0fb87edaef..e1ac5e03ff 100644 --- a/spec/support/crypt/encrypted_fields/range-encryptedFields-Long.json +++ b/spec/support/crypt/encrypted_fields/range-encryptedFields-Long.json @@ -10,7 +10,7 @@ "path": "encryptedLong", "bsonType": "long", "queries": { - "queryType": "rangePreview", + "queryType": "range", "sparsity": { "$numberLong": "1" }, diff --git a/spec/support/shared/session.rb b/spec/support/shared/session.rb index 7188f5f883..acbc3a2ceb 100644 --- a/spec/support/shared/session.rb +++ b/spec/support/shared/session.rb @@ -110,8 +110,8 @@ end it 'raises an error' do - expect([Mongo::Error::OperationFailure, - Mongo::Error::BulkWriteError]).to include(operation_result.class) + expect([Mongo::Error::OperationFailure::Family, + Mongo::Error::BulkWriteError].any? { |e| e === operation_result }).to be true end it 'updates the last use value' do diff --git a/spec/support/spec_setup.rb b/spec/support/spec_setup.rb index dcfa83a533..442a7352cd 100644 --- a/spec/support/spec_setup.rb +++ b/spec/support/spec_setup.rb @@ -28,7 +28,7 @@ def run # more users to any other databases. begin create_user(client, SpecConfig.instance.root_user) - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e # When testing a cluster that requires auth, root user is already set up # and it is not creatable without auth. # Seems like every mongodb version has its own error message @@ -61,7 +61,7 @@ def create_user(client, user) users = client.use('admin').database.users begin users.create(user) - rescue Mongo::Error::OperationFailure => e + rescue Mongo::Error::OperationFailure::Family => e if e.message =~ /User.*already exists/ users.remove(user.name) users.create(user) diff --git a/spec/support/utils.rb b/spec/support/utils.rb index fa572adaa6..d0913d101a 100644 --- a/spec/support/utils.rb +++ b/spec/support/utils.rb @@ -330,8 +330,10 @@ def match_with_type?(expected, actual) when 'long' expected_class = BSON::Int64 expected_key = '$numberLong' + when %w[int long] + return actual.is_a?(Numeric) || actual.is_a?(BSON::Int32) || actual.is_a?(BSON::Int64) else - raise "Tests do not currently support matching against $$type #{v['$$type']}" + raise "Tests do not currently support matching against $$type #{expected['$$type']}" end actual.is_a?(expected_class) || actual.key?(expected_key)