mongo 2.20.1 → 2.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +3 -0
- data/Rakefile +2 -2
- data/lib/mongo/address.rb +22 -3
- data/lib/mongo/auth/aws/credentials_retriever.rb +70 -17
- data/lib/mongo/auth/base.rb +1 -1
- data/lib/mongo/bulk_write.rb +35 -2
- data/lib/mongo/client.rb +38 -6
- data/lib/mongo/client_encryption.rb +6 -3
- data/lib/mongo/cluster/reapers/cursor_reaper.rb +6 -1
- data/lib/mongo/cluster/sdam_flow.rb +20 -7
- data/lib/mongo/cluster.rb +14 -4
- data/lib/mongo/collection/helpers.rb +1 -1
- data/lib/mongo/collection/view/aggregation/behavior.rb +131 -0
- data/lib/mongo/collection/view/aggregation.rb +33 -99
- data/lib/mongo/collection/view/builder/aggregation.rb +1 -7
- data/lib/mongo/collection/view/change_stream.rb +80 -27
- data/lib/mongo/collection/view/iterable.rb +76 -60
- data/lib/mongo/collection/view/map_reduce.rb +25 -8
- data/lib/mongo/collection/view/readable.rb +79 -30
- data/lib/mongo/collection/view/writable.rb +109 -48
- data/lib/mongo/collection/view.rb +43 -3
- data/lib/mongo/collection.rb +158 -23
- data/lib/mongo/crypt/auto_encrypter.rb +4 -6
- data/lib/mongo/crypt/binding.rb +4 -4
- data/lib/mongo/crypt/context.rb +20 -14
- data/lib/mongo/crypt/encryption_io.rb +56 -26
- data/lib/mongo/crypt/explicit_encrypter.rb +49 -20
- data/lib/mongo/crypt/explicit_encryption_context.rb +17 -11
- data/lib/mongo/crypt/kms/azure/credentials_retriever.rb +22 -6
- data/lib/mongo/crypt/kms/gcp/credentials_retriever.rb +29 -4
- data/lib/mongo/csot_timeout_holder.rb +119 -0
- data/lib/mongo/cursor/kill_spec.rb +5 -2
- data/lib/mongo/cursor/nontailable.rb +27 -0
- data/lib/mongo/cursor.rb +86 -24
- data/lib/mongo/cursor_host.rb +82 -0
- data/lib/mongo/database/view.rb +81 -14
- data/lib/mongo/database.rb +88 -18
- data/lib/mongo/error/operation_failure.rb +209 -204
- data/lib/mongo/error/server_timeout_error.rb +12 -0
- data/lib/mongo/error/socket_timeout_error.rb +3 -1
- data/lib/mongo/error/timeout_error.rb +23 -0
- data/lib/mongo/error.rb +2 -0
- data/lib/mongo/grid/fs_bucket.rb +45 -12
- data/lib/mongo/grid/stream/read.rb +15 -1
- data/lib/mongo/grid/stream/write.rb +21 -4
- data/lib/mongo/index/view.rb +77 -16
- data/lib/mongo/operation/context.rb +40 -2
- data/lib/mongo/operation/create_search_indexes/op_msg.rb +2 -2
- data/lib/mongo/operation/delete/op_msg.rb +2 -1
- data/lib/mongo/operation/drop_search_index/op_msg.rb +2 -2
- data/lib/mongo/operation/find/op_msg.rb +45 -0
- data/lib/mongo/operation/get_more/op_msg.rb +33 -0
- data/lib/mongo/operation/insert/op_msg.rb +3 -2
- data/lib/mongo/operation/insert/result.rb +4 -2
- data/lib/mongo/operation/list_collections/result.rb +1 -1
- data/lib/mongo/operation/map_reduce/result.rb +1 -1
- data/lib/mongo/operation/op_msg_base.rb +3 -1
- data/lib/mongo/operation/result.rb +26 -5
- data/lib/mongo/operation/shared/executable.rb +12 -1
- data/lib/mongo/operation/shared/op_msg_executable.rb +4 -1
- data/lib/mongo/operation/shared/response_handling.rb +3 -3
- data/lib/mongo/operation/shared/sessions_supported.rb +1 -1
- data/lib/mongo/operation/shared/timed.rb +52 -0
- data/lib/mongo/operation/shared/write.rb +4 -1
- data/lib/mongo/operation/update/op_msg.rb +2 -1
- data/lib/mongo/operation/update_search_index/op_msg.rb +2 -2
- data/lib/mongo/operation.rb +1 -0
- data/lib/mongo/protocol/message.rb +1 -4
- data/lib/mongo/protocol/msg.rb +2 -2
- data/lib/mongo/retryable/read_worker.rb +69 -29
- data/lib/mongo/retryable/write_worker.rb +49 -18
- data/lib/mongo/retryable.rb +8 -2
- data/lib/mongo/server/connection.rb +11 -5
- data/lib/mongo/server/connection_base.rb +22 -2
- data/lib/mongo/server/connection_pool.rb +32 -14
- data/lib/mongo/server/description/features.rb +1 -1
- data/lib/mongo/server/description.rb +18 -5
- data/lib/mongo/server/monitor.rb +7 -4
- data/lib/mongo/server/pending_connection.rb +7 -3
- data/lib/mongo/server/{round_trip_time_averager.rb → round_trip_time_calculator.rb} +25 -7
- data/lib/mongo/server.rb +11 -6
- data/lib/mongo/server_selector/base.rb +25 -9
- data/lib/mongo/session.rb +78 -9
- data/lib/mongo/socket/ssl.rb +109 -17
- data/lib/mongo/socket/tcp.rb +40 -6
- data/lib/mongo/socket.rb +154 -25
- data/lib/mongo/uri/options_mapper.rb +1 -0
- data/lib/mongo/version.rb +1 -1
- data/lib/mongo.rb +1 -0
- data/spec/atlas/atlas_connectivity_spec.rb +4 -0
- data/spec/atlas/operations_spec.rb +4 -0
- data/spec/integration/client_side_encryption/auto_encryption_mongocryptd_spawn_spec.rb +2 -1
- data/spec/integration/client_side_encryption/auto_encryption_spec.rb +494 -487
- data/spec/integration/client_side_encryption/on_demand_aws_credentials_spec.rb +1 -1
- data/spec/integration/client_side_encryption/range_explicit_encryption_prose_spec.rb +66 -22
- data/spec/integration/client_side_operations_timeout/encryption_prose_spec.rb +131 -0
- data/spec/integration/connection_pool_populator_spec.rb +2 -0
- data/spec/integration/cursor_pinning_spec.rb +15 -60
- data/spec/integration/cursor_reaping_spec.rb +1 -1
- data/spec/integration/docs_examples_spec.rb +1 -1
- data/spec/integration/operation_failure_code_spec.rb +1 -1
- data/spec/integration/operation_failure_message_spec.rb +3 -3
- data/spec/integration/retryable_errors_spec.rb +2 -2
- data/spec/integration/sdam_error_handling_spec.rb +2 -1
- data/spec/integration/search_indexes_prose_spec.rb +4 -0
- data/spec/integration/server_spec.rb +4 -3
- data/spec/integration/transactions_api_examples_spec.rb +2 -0
- data/spec/kerberos/kerberos_spec.rb +4 -0
- data/spec/lite_spec_helper.rb +3 -1
- data/spec/mongo/auth/user/view_spec.rb +1 -1
- data/spec/mongo/caching_cursor_spec.rb +1 -1
- data/spec/mongo/client_encryption_spec.rb +1 -0
- data/spec/mongo/client_spec.rb +158 -4
- data/spec/mongo/collection/view/aggregation_spec.rb +14 -39
- data/spec/mongo/collection/view/change_stream_spec.rb +3 -3
- data/spec/mongo/collection_spec.rb +5 -6
- data/spec/mongo/crypt/auto_encrypter_spec.rb +14 -12
- data/spec/mongo/crypt/data_key_context_spec.rb +3 -1
- data/spec/mongo/crypt/explicit_encryption_context_spec.rb +2 -2
- data/spec/mongo/crypt/handle_spec.rb +1 -1
- data/spec/mongo/cursor_spec.rb +26 -9
- data/spec/mongo/error/operation_failure_heavy_spec.rb +2 -2
- data/spec/mongo/operation/context_spec.rb +79 -0
- data/spec/mongo/operation/create/op_msg_spec.rb +106 -110
- data/spec/mongo/operation/delete/op_msg_spec.rb +6 -5
- data/spec/mongo/operation/find/op_msg_spec.rb +66 -0
- data/spec/mongo/operation/get_more/op_msg_spec.rb +65 -0
- data/spec/mongo/operation/insert/op_msg_spec.rb +128 -131
- data/spec/mongo/operation/shared/csot/examples.rb +113 -0
- data/spec/mongo/query_cache_spec.rb +243 -225
- data/spec/mongo/retryable_spec.rb +1 -0
- data/spec/mongo/server/round_trip_time_calculator_spec.rb +120 -0
- data/spec/mongo/socket/ssl_spec.rb +0 -10
- data/spec/runners/change_streams/test.rb +2 -2
- data/spec/runners/crud/operation.rb +1 -1
- data/spec/runners/crud/verifier.rb +3 -1
- data/spec/runners/transactions/operation.rb +4 -6
- data/spec/runners/unified/ambiguous_operations.rb +13 -0
- data/spec/runners/unified/assertions.rb +4 -0
- data/spec/runners/unified/change_stream_operations.rb +14 -24
- data/spec/runners/unified/crud_operations.rb +82 -59
- data/spec/runners/unified/ddl_operations.rb +38 -7
- data/spec/runners/unified/grid_fs_operations.rb +37 -2
- data/spec/runners/unified/support_operations.rb +43 -4
- data/spec/runners/unified/test.rb +22 -10
- data/spec/runners/unified.rb +1 -1
- data/spec/solo/clean_exit_spec.rb +2 -0
- data/spec/spec_tests/client_side_operations_timeout_spec.rb +15 -0
- data/spec/spec_tests/data/change_streams_unified/change-streams-clusterTime.yml +3 -1
- data/spec/spec_tests/data/change_streams_unified/change-streams-disambiguatedPaths.yml +3 -1
- data/spec/spec_tests/data/change_streams_unified/change-streams-errors.yml +3 -1
- data/spec/spec_tests/data/change_streams_unified/change-streams-pre_and_post_images.yml +1 -1
- data/spec/spec_tests/data/change_streams_unified/change-streams-resume-allowlist.yml +1 -1
- data/spec/spec_tests/data/change_streams_unified/change-streams-resume-errorLabels.yml +1 -1
- data/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml +1 -1
- data/spec/spec_tests/data/client_side_encryption/badQueries.yml +2 -1
- data/spec/spec_tests/data/client_side_encryption/timeoutMS.yml +67 -0
- data/spec/spec_tests/data/client_side_operations_timeout/bulkWrite.yml +87 -0
- data/spec/spec_tests/data/client_side_operations_timeout/change-streams.yml +358 -0
- data/spec/spec_tests/data/client_side_operations_timeout/close-cursors.yml +129 -0
- data/spec/spec_tests/data/client_side_operations_timeout/command-execution.yml +250 -0
- data/spec/spec_tests/data/client_side_operations_timeout/convenient-transactions.yml +113 -0
- data/spec/spec_tests/data/client_side_operations_timeout/cursors.yml +70 -0
- data/spec/spec_tests/data/client_side_operations_timeout/deprecated-options.yml +3982 -0
- data/spec/spec_tests/data/client_side_operations_timeout/error-transformations.yml +96 -0
- data/spec/spec_tests/data/client_side_operations_timeout/global-timeoutMS.yml +3236 -0
- data/spec/spec_tests/data/client_side_operations_timeout/gridfs-advanced.yml +207 -0
- data/spec/spec_tests/data/client_side_operations_timeout/gridfs-delete.yml +152 -0
- data/spec/spec_tests/data/client_side_operations_timeout/gridfs-download.yml +182 -0
- data/spec/spec_tests/data/client_side_operations_timeout/gridfs-find.yml +100 -0
- data/spec/spec_tests/data/client_side_operations_timeout/gridfs-upload.yml +249 -0
- data/spec/spec_tests/data/client_side_operations_timeout/legacy-timeouts.yml +204 -0
- data/spec/spec_tests/data/client_side_operations_timeout/non-tailable-cursors.yml +307 -0
- data/spec/spec_tests/data/client_side_operations_timeout/override-collection-timeoutMS.yml +1877 -0
- data/spec/spec_tests/data/client_side_operations_timeout/override-operation-timeoutMS.yml +1918 -0
- data/spec/spec_tests/data/client_side_operations_timeout/retryability-legacy-timeouts.yml +1676 -0
- data/spec/spec_tests/data/client_side_operations_timeout/retryability-timeoutMS.yml +2824 -0
- data/spec/spec_tests/data/client_side_operations_timeout/sessions-inherit-timeoutMS.yml +168 -0
- data/spec/spec_tests/data/client_side_operations_timeout/sessions-override-operation-timeoutMS.yml +171 -0
- data/spec/spec_tests/data/client_side_operations_timeout/sessions-override-timeoutMS.yml +168 -0
- data/spec/spec_tests/data/client_side_operations_timeout/tailable-awaitData.yml +247 -0
- data/spec/spec_tests/data/client_side_operations_timeout/tailable-non-awaitData.yml +181 -0
- data/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml +4 -0
- data/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml +4 -0
- data/spec/spec_tests/data/crud_unified/find-test-all-options.yml +29 -0
- data/spec/spec_tests/server_selection_rtt_spec.rb +6 -6
- data/spec/support/certificates/atlas-ocsp-ca.crt +81 -83
- data/spec/support/certificates/atlas-ocsp.crt +107 -107
- data/spec/support/cluster_tools.rb +3 -3
- data/spec/support/common_shortcuts.rb +2 -2
- data/spec/support/crypt/encrypted_fields/range-encryptedFields-Date.json +1 -1
- data/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalNoPrecision.json +1 -1
- data/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalPrecision.json +1 -1
- data/spec/support/crypt/encrypted_fields/range-encryptedFields-DoubleNoPrecision.json +1 -1
- data/spec/support/crypt/encrypted_fields/range-encryptedFields-DoublePrecision.json +1 -1
- data/spec/support/crypt/encrypted_fields/range-encryptedFields-Int.json +1 -1
- data/spec/support/crypt/encrypted_fields/range-encryptedFields-Long.json +1 -1
- data/spec/support/shared/session.rb +2 -2
- data/spec/support/spec_setup.rb +2 -2
- data/spec/support/utils.rb +3 -1
- metadata +78 -91
- data/spec/mongo/server/round_trip_time_averager_spec.rb +0 -48
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Aggregate.yml +0 -242
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Correctness.yml +0 -423
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Delete.yml +0 -183
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-FindOneAndUpdate.yml +0 -240
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-InsertFind.yml +0 -236
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Update.yml +0 -253
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Aggregate.yml +0 -1688
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Correctness.yml +0 -294
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Delete.yml +0 -906
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-FindOneAndUpdate.yml +0 -1685
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-InsertFind.yml +0 -1681
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Update.yml +0 -1698
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Aggregate.yml +0 -330
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Correctness.yml +0 -425
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Delete.yml +0 -227
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.yml +0 -328
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-InsertFind.yml +0 -320
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Update.yml +0 -337
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Aggregate.yml +0 -914
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Correctness.yml +0 -293
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Delete.yml +0 -519
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-FindOneAndUpdate.yml +0 -912
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-InsertFind.yml +0 -908
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Update.yml +0 -925
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Aggregate.yml +0 -326
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Correctness.yml +0 -425
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Delete.yml +0 -225
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-FindOneAndUpdate.yml +0 -324
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-InsertFind.yml +0 -320
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Update.yml +0 -339
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Aggregate.yml +0 -242
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Correctness.yml +0 -424
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Delete.yml +0 -183
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-FindOneAndUpdate.yml +0 -240
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-InsertFind.yml +0 -236
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Update.yml +0 -255
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Aggregate.yml +0 -242
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Correctness.yml +0 -423
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Delete.yml +0 -183
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-FindOneAndUpdate.yml +0 -240
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-InsertFind.yml +0 -236
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Update.yml +0 -255
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-WrongType.yml +0 -44
| @@ -0,0 +1,131 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Mongo
         | 
| 4 | 
            +
              class Collection
         | 
| 5 | 
            +
                class View
         | 
| 6 | 
            +
                  class Aggregation
         | 
| 7 | 
            +
                    # Distills the behavior common to aggregator classes, like
         | 
| 8 | 
            +
                    # View::Aggregator and View::ChangeStream.
         | 
| 9 | 
            +
                    module Behavior
         | 
| 10 | 
            +
                      extend Forwardable
         | 
| 11 | 
            +
                      include Enumerable
         | 
| 12 | 
            +
                      include Immutable
         | 
| 13 | 
            +
                      include Iterable
         | 
| 14 | 
            +
                      include Explainable
         | 
| 15 | 
            +
                      include Loggable
         | 
| 16 | 
            +
                      include Retryable
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                      # @return [ View ] view The collection view.
         | 
| 19 | 
            +
                      attr_reader :view
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                      # Delegate necessary operations to the view.
         | 
| 22 | 
            +
                      def_delegators :view, :collection, :read, :cluster, :cursor_type, :limit, :batch_size
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                      # Delegate necessary operations to the collection.
         | 
| 25 | 
            +
                      def_delegators :collection, :database, :client
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                      # Set to true if disk usage is allowed during the aggregation.
         | 
| 28 | 
            +
                      #
         | 
| 29 | 
            +
                      # @example Set disk usage flag.
         | 
| 30 | 
            +
                      #   aggregation.allow_disk_use(true)
         | 
| 31 | 
            +
                      #
         | 
| 32 | 
            +
                      # @param [ true, false ] value The flag value.
         | 
| 33 | 
            +
                      #
         | 
| 34 | 
            +
                      # @return [ true, false, Aggregation ] The aggregation if a value was
         | 
| 35 | 
            +
                      #   set or the value if used as a getter.
         | 
| 36 | 
            +
                      #
         | 
| 37 | 
            +
                      # @since 2.0.0
         | 
| 38 | 
            +
                      def allow_disk_use(value = nil)
         | 
| 39 | 
            +
                        configure(:allow_disk_use, value)
         | 
| 40 | 
            +
                      end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                      # Get the explain plan for the aggregation.
         | 
| 43 | 
            +
                      #
         | 
| 44 | 
            +
                      # @example Get the explain plan for the aggregation.
         | 
| 45 | 
            +
                      #   aggregation.explain
         | 
| 46 | 
            +
                      #
         | 
| 47 | 
            +
                      # @return [ Hash ] The explain plan.
         | 
| 48 | 
            +
                      #
         | 
| 49 | 
            +
                      # @since 2.0.0
         | 
| 50 | 
            +
                      def explain
         | 
| 51 | 
            +
                        self.class.new(view, pipeline, options.merge(explain: true)).first
         | 
| 52 | 
            +
                      end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                      # Whether this aggregation will write its result to a database collection.
         | 
| 55 | 
            +
                      #
         | 
| 56 | 
            +
                      # @return [ Boolean ] Whether the aggregation will write its result
         | 
| 57 | 
            +
                      #   to a collection.
         | 
| 58 | 
            +
                      #
         | 
| 59 | 
            +
                      # @api private
         | 
| 60 | 
            +
                      def write?
         | 
| 61 | 
            +
                        pipeline.any? { |op| op.key?('$out') || op.key?(:$out) || op.key?('$merge') || op.key?(:$merge) }
         | 
| 62 | 
            +
                      end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                      # @return [ Integer | nil ] the timeout_ms value that was passed as
         | 
| 65 | 
            +
                      #   an option to this object, or which was inherited from the view.
         | 
| 66 | 
            +
                      #
         | 
| 67 | 
            +
                      # @api private
         | 
| 68 | 
            +
                      def timeout_ms
         | 
| 69 | 
            +
                        @timeout_ms || view.timeout_ms
         | 
| 70 | 
            +
                      end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                      private
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                      # Common setup for all classes that include this behavior; the
         | 
| 75 | 
            +
                      # constructor should invoke this method.
         | 
| 76 | 
            +
                      def perform_setup(view, options, forbid: [])
         | 
| 77 | 
            +
                        @view = view
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                        @timeout_ms = options.delete(:timeout_ms)
         | 
| 80 | 
            +
                        @options = BSON::Document.new(options).freeze
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                        yield
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                        validate_timeout_mode!(options, forbid: forbid)
         | 
| 85 | 
            +
                      end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                      def server_selector
         | 
| 88 | 
            +
                        @view.send(:server_selector)
         | 
| 89 | 
            +
                      end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                      def aggregate_spec(session, read_preference)
         | 
| 92 | 
            +
                        Builder::Aggregation.new(
         | 
| 93 | 
            +
                          pipeline,
         | 
| 94 | 
            +
                          view,
         | 
| 95 | 
            +
                          options.merge(session: session, read_preference: read_preference)
         | 
| 96 | 
            +
                        ).specification
         | 
| 97 | 
            +
                      end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                      # Skip, sort, limit, projection are specified as pipeline stages
         | 
| 100 | 
            +
                      # rather than as options.
         | 
| 101 | 
            +
                      def cache_options
         | 
| 102 | 
            +
                        {
         | 
| 103 | 
            +
                          namespace: collection.namespace,
         | 
| 104 | 
            +
                          selector: pipeline,
         | 
| 105 | 
            +
                          read_concern: view.read_concern,
         | 
| 106 | 
            +
                          read_preference: view.read_preference,
         | 
| 107 | 
            +
                          collation: options[:collation],
         | 
| 108 | 
            +
                          # Aggregations can read documents from more than one collection,
         | 
| 109 | 
            +
                          # so they will be cleared on every write operation.
         | 
| 110 | 
            +
                          multi_collection: true,
         | 
| 111 | 
            +
                        }
         | 
| 112 | 
            +
                      end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                      # @return [ Hash ] timeout_ms value set on the operation level (if any),
         | 
| 115 | 
            +
                      #   and/or timeout_ms that is set on collection/database/client level (if any).
         | 
| 116 | 
            +
                      #
         | 
| 117 | 
            +
                      # @api private
         | 
| 118 | 
            +
                      def operation_timeouts(opts = {})
         | 
| 119 | 
            +
                        {}.tap do |result|
         | 
| 120 | 
            +
                          if opts[:timeout_ms] || @timeout_ms
         | 
| 121 | 
            +
                            result[:operation_timeout_ms] = opts.delete(:timeout_ms) || @timeout_ms
         | 
| 122 | 
            +
                          else
         | 
| 123 | 
            +
                            result[:inherited_timeout_ms] = view.timeout_ms
         | 
| 124 | 
            +
                          end
         | 
| 125 | 
            +
                        end
         | 
| 126 | 
            +
                      end
         | 
| 127 | 
            +
                    end
         | 
| 128 | 
            +
                  end
         | 
| 129 | 
            +
                end
         | 
| 130 | 
            +
              end
         | 
| 131 | 
            +
            end
         | 
| @@ -15,6 +15,8 @@ | |
| 15 15 | 
             
            # See the License for the specific language governing permissions and
         | 
| 16 16 | 
             
            # limitations under the License.
         | 
| 17 17 |  | 
| 18 | 
            +
            require 'mongo/collection/view/aggregation/behavior'
         | 
| 19 | 
            +
             | 
| 18 20 | 
             
            module Mongo
         | 
| 19 21 | 
             
              class Collection
         | 
| 20 22 | 
             
                class View
         | 
| @@ -23,46 +25,11 @@ module Mongo | |
| 23 25 | 
             
                  #
         | 
| 24 26 | 
             
                  # @since 2.0.0
         | 
| 25 27 | 
             
                  class Aggregation
         | 
| 26 | 
            -
                     | 
| 27 | 
            -
                    include Enumerable
         | 
| 28 | 
            -
                    include Immutable
         | 
| 29 | 
            -
                    include Iterable
         | 
| 30 | 
            -
                    include Explainable
         | 
| 31 | 
            -
                    include Loggable
         | 
| 32 | 
            -
                    include Retryable
         | 
| 28 | 
            +
                    include Behavior
         | 
| 33 29 |  | 
| 34 | 
            -
                    # @return [ View ] view The collection view.
         | 
| 35 | 
            -
                    attr_reader :view
         | 
| 36 30 | 
             
                    # @return [ Array<Hash> ] pipeline The aggregation pipeline.
         | 
| 37 31 | 
             
                    attr_reader :pipeline
         | 
| 38 32 |  | 
| 39 | 
            -
                    # Delegate necessary operations to the view.
         | 
| 40 | 
            -
                    def_delegators :view, :collection, :read, :cluster
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                    # Delegate necessary operations to the collection.
         | 
| 43 | 
            -
                    def_delegators :collection, :database, :client
         | 
| 44 | 
            -
             | 
| 45 | 
            -
                    # The reroute message.
         | 
| 46 | 
            -
                    #
         | 
| 47 | 
            -
                    # @since 2.1.0
         | 
| 48 | 
            -
                    # @deprecated
         | 
| 49 | 
            -
                    REROUTE = 'Rerouting the Aggregation operation to the primary server.'.freeze
         | 
| 50 | 
            -
             | 
| 51 | 
            -
                    # Set to true if disk usage is allowed during the aggregation.
         | 
| 52 | 
            -
                    #
         | 
| 53 | 
            -
                    # @example Set disk usage flag.
         | 
| 54 | 
            -
                    #   aggregation.allow_disk_use(true)
         | 
| 55 | 
            -
                    #
         | 
| 56 | 
            -
                    # @param [ true, false ] value The flag value.
         | 
| 57 | 
            -
                    #
         | 
| 58 | 
            -
                    # @return [ true, false, Aggregation ] The aggregation if a value was
         | 
| 59 | 
            -
                    #   set or the value if used as a getter.
         | 
| 60 | 
            -
                    #
         | 
| 61 | 
            -
                    # @since 2.0.0
         | 
| 62 | 
            -
                    def allow_disk_use(value = nil)
         | 
| 63 | 
            -
                      configure(:allow_disk_use, value)
         | 
| 64 | 
            -
                    end
         | 
| 65 | 
            -
             | 
| 66 33 | 
             
                    # Initialize the aggregation for the provided collection view, pipeline
         | 
| 67 34 | 
             
                    # and options.
         | 
| 68 35 | 
             
                    #
         | 
| @@ -86,59 +53,29 @@ module Mongo | |
| 86 53 | 
             
                    # @option options [ Hash ] :let Mapping of variables to use in the pipeline.
         | 
| 87 54 | 
             
                    #   See the server documentation for details.
         | 
| 88 55 | 
             
                    # @option options [ Integer ] :max_time_ms The maximum amount of time in
         | 
| 89 | 
            -
                    #   milliseconds to allow the aggregation to run.
         | 
| 90 | 
            -
                    # | 
| 91 | 
            -
                    #   will request that the server provide results using a cursor. Note that
         | 
| 92 | 
            -
                    #   as of server version 3.6, aggregations always provide results using a
         | 
| 93 | 
            -
                    #   cursor and this option is therefore not valid.
         | 
| 56 | 
            +
                    #   milliseconds to allow the aggregation to run. This option is deprecated, use
         | 
| 57 | 
            +
                    #   :timeout_ms instead.
         | 
| 94 58 | 
             
                    # @option options [ Session ] :session The session to use.
         | 
| 59 | 
            +
                    # @option options [ :cursor_lifetime | :iteration ] :timeout_mode How to interpret
         | 
| 60 | 
            +
                    #   :timeout_ms (whether it applies to the lifetime of the cursor, or per
         | 
| 61 | 
            +
                    #   iteration).
         | 
| 62 | 
            +
                    # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds.
         | 
| 63 | 
            +
                    #    Must be a non-negative integer. An explicit value of 0 means infinite.
         | 
| 64 | 
            +
                    #    The default value is unset which means the value is inherited from
         | 
| 65 | 
            +
                    #    the collection or the database or the client.
         | 
| 95 66 | 
             
                    #
         | 
| 96 67 | 
             
                    # @since 2.0.0
         | 
| 97 68 | 
             
                    def initialize(view, pipeline, options = {})
         | 
| 98 | 
            -
                       | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 69 | 
            +
                      perform_setup(view, options) do
         | 
| 70 | 
            +
                        @pipeline = pipeline.dup
         | 
| 71 | 
            +
                        unless Mongo.broken_view_aggregate || view.filter.empty?
         | 
| 72 | 
            +
                          @pipeline.unshift(:$match => view.filter)
         | 
| 73 | 
            +
                        end
         | 
| 102 74 | 
             
                      end
         | 
| 103 | 
            -
                      @options = BSON::Document.new(options).freeze
         | 
| 104 | 
            -
                    end
         | 
| 105 | 
            -
             | 
| 106 | 
            -
                    # Get the explain plan for the aggregation.
         | 
| 107 | 
            -
                    #
         | 
| 108 | 
            -
                    # @example Get the explain plan for the aggregation.
         | 
| 109 | 
            -
                    #   aggregation.explain
         | 
| 110 | 
            -
                    #
         | 
| 111 | 
            -
                    # @return [ Hash ] The explain plan.
         | 
| 112 | 
            -
                    #
         | 
| 113 | 
            -
                    # @since 2.0.0
         | 
| 114 | 
            -
                    def explain
         | 
| 115 | 
            -
                      self.class.new(view, pipeline, options.merge(explain: true)).first
         | 
| 116 | 
            -
                    end
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                    # Whether this aggregation will write its result to a database collection.
         | 
| 119 | 
            -
                    #
         | 
| 120 | 
            -
                    # @return [ Boolean ] Whether the aggregation will write its result
         | 
| 121 | 
            -
                    #   to a collection.
         | 
| 122 | 
            -
                    #
         | 
| 123 | 
            -
                    # @api private
         | 
| 124 | 
            -
                    def write?
         | 
| 125 | 
            -
                      pipeline.any? { |op| op.key?('$out') || op.key?(:$out) || op.key?('$merge') || op.key?(:$merge) }
         | 
| 126 75 | 
             
                    end
         | 
| 127 76 |  | 
| 128 77 | 
             
                    private
         | 
| 129 78 |  | 
| 130 | 
            -
                    def server_selector
         | 
| 131 | 
            -
                      @view.send(:server_selector)
         | 
| 132 | 
            -
                    end
         | 
| 133 | 
            -
             | 
| 134 | 
            -
                    def aggregate_spec(session, read_preference)
         | 
| 135 | 
            -
                      Builder::Aggregation.new(
         | 
| 136 | 
            -
                        pipeline,
         | 
| 137 | 
            -
                        view,
         | 
| 138 | 
            -
                        options.merge(session: session, read_preference: read_preference)
         | 
| 139 | 
            -
                      ).specification
         | 
| 140 | 
            -
                    end
         | 
| 141 | 
            -
             | 
| 142 79 | 
             
                    def new(options)
         | 
| 143 80 | 
             
                      Aggregation.new(view, pipeline, options)
         | 
| 144 81 | 
             
                    end
         | 
| @@ -180,32 +117,29 @@ module Mongo | |
| 180 117 |  | 
| 181 118 | 
             
                    end
         | 
| 182 119 |  | 
| 183 | 
            -
                    def send_initial_query(server,  | 
| 184 | 
            -
                      server. | 
| 120 | 
            +
                    def send_initial_query(server, context)
         | 
| 121 | 
            +
                      if server.load_balancer?
         | 
| 122 | 
            +
                        # Connection will be checked in when cursor is drained.
         | 
| 123 | 
            +
                        connection = server.pool.check_out(context: context)
         | 
| 185 124 | 
             
                        initial_query_op(
         | 
| 186 | 
            -
                          session,
         | 
| 125 | 
            +
                          context.session,
         | 
| 187 126 | 
             
                          effective_read_preference(connection)
         | 
| 188 127 | 
             
                        ).execute_with_connection(
         | 
| 189 128 | 
             
                          connection,
         | 
| 190 | 
            -
                          context:  | 
| 129 | 
            +
                          context: context
         | 
| 191 130 | 
             
                        )
         | 
| 131 | 
            +
                      else
         | 
| 132 | 
            +
                        server.with_connection do |connection|
         | 
| 133 | 
            +
                          initial_query_op(
         | 
| 134 | 
            +
                            context.session,
         | 
| 135 | 
            +
                            effective_read_preference(connection)
         | 
| 136 | 
            +
                          ).execute_with_connection(
         | 
| 137 | 
            +
                            connection,
         | 
| 138 | 
            +
                            context: context
         | 
| 139 | 
            +
                          )
         | 
| 140 | 
            +
                        end
         | 
| 192 141 | 
             
                      end
         | 
| 193 142 | 
             
                    end
         | 
| 194 | 
            -
             | 
| 195 | 
            -
                    # Skip, sort, limit, projection are specified as pipeline stages
         | 
| 196 | 
            -
                    # rather than as options.
         | 
| 197 | 
            -
                    def cache_options
         | 
| 198 | 
            -
                      {
         | 
| 199 | 
            -
                        namespace: collection.namespace,
         | 
| 200 | 
            -
                        selector: pipeline,
         | 
| 201 | 
            -
                        read_concern: view.read_concern,
         | 
| 202 | 
            -
                        read_preference: view.read_preference,
         | 
| 203 | 
            -
                        collation: options[:collation],
         | 
| 204 | 
            -
                        # Aggregations can read documents from more than one collection,
         | 
| 205 | 
            -
                        # so they will be cleared on every write operation.
         | 
| 206 | 
            -
                        multi_collection: true,
         | 
| 207 | 
            -
                      }
         | 
| 208 | 
            -
                    end
         | 
| 209 143 | 
             
                  end
         | 
| 210 144 | 
             
                end
         | 
| 211 145 | 
             
              end
         | 
| @@ -113,17 +113,11 @@ module Mongo | |
| 113 113 | 
             
                          command[:readConcern] = Options::Mapper.transform_values_to_strings(
         | 
| 114 114 | 
             
                            read_concern)
         | 
| 115 115 | 
             
                        end
         | 
| 116 | 
            -
                        command[:cursor] =  | 
| 116 | 
            +
                        command[:cursor] = batch_size_doc
         | 
| 117 117 | 
             
                        command.merge!(Options::Mapper.transform_documents(options, MAPPINGS))
         | 
| 118 118 | 
             
                        command
         | 
| 119 119 | 
             
                      end
         | 
| 120 120 |  | 
| 121 | 
            -
                      def cursor
         | 
| 122 | 
            -
                        if options[:use_cursor] == true || options[:use_cursor].nil?
         | 
| 123 | 
            -
                          batch_size_doc
         | 
| 124 | 
            -
                        end
         | 
| 125 | 
            -
                      end
         | 
| 126 | 
            -
             | 
| 127 121 | 
             
                      def batch_size_doc
         | 
| 128 122 | 
             
                        value = options[:batch_size] || view.batch_size
         | 
| 129 123 | 
             
                        if value == 0 && write?
         | 
| @@ -15,6 +15,7 @@ | |
| 15 15 | 
             
            # See the License for the specific language governing permissions and
         | 
| 16 16 | 
             
            # limitations under the License.
         | 
| 17 17 |  | 
| 18 | 
            +
            require 'mongo/collection/view/aggregation/behavior'
         | 
| 18 19 | 
             
            require 'mongo/collection/view/change_stream/retryable'
         | 
| 19 20 |  | 
| 20 21 | 
             
            module Mongo
         | 
| @@ -35,7 +36,8 @@ module Mongo | |
| 35 36 | 
             
                  #
         | 
| 36 37 | 
             
                  #
         | 
| 37 38 | 
             
                  # @since 2.5.0
         | 
| 38 | 
            -
                  class ChangeStream | 
| 39 | 
            +
                  class ChangeStream
         | 
| 40 | 
            +
                    include Aggregation::Behavior
         | 
| 39 41 | 
             
                    include Retryable
         | 
| 40 42 |  | 
| 41 43 | 
             
                    # @return [ String ] The fullDocument option default value.
         | 
| @@ -60,6 +62,10 @@ module Mongo | |
| 60 62 | 
             
                    # @since 2.5.0
         | 
| 61 63 | 
             
                    attr_reader :options
         | 
| 62 64 |  | 
| 65 | 
            +
                    # @return [ Cursor ] the underlying cursor for this operation
         | 
| 66 | 
            +
                    # @api private
         | 
| 67 | 
            +
                    attr_reader :cursor
         | 
| 68 | 
            +
             | 
| 63 69 | 
             
                    # Initialize the change stream for the provided collection view, pipeline
         | 
| 64 70 | 
             
                    # and options.
         | 
| 65 71 | 
             
                    #
         | 
| @@ -125,11 +131,13 @@ module Mongo | |
| 125 131 | 
             
                    #
         | 
| 126 132 | 
             
                    # @since 2.5.0
         | 
| 127 133 | 
             
                    def initialize(view, pipeline, changes_for, options = {})
         | 
| 128 | 
            -
                       | 
| 129 | 
            -
                       | 
| 130 | 
            -
                       | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 134 | 
            +
                      # change stream cursors can only be :iterable, so we don't allow
         | 
| 135 | 
            +
                      # timeout_mode to be specified.
         | 
| 136 | 
            +
                      perform_setup(view, options, forbid: %i[ timeout_mode ]) do
         | 
| 137 | 
            +
                        @changes_for = changes_for
         | 
| 138 | 
            +
                        @change_stream_filters = pipeline && pipeline.dup
         | 
| 139 | 
            +
                        @start_after = @options[:start_after]
         | 
| 140 | 
            +
                      end
         | 
| 133 141 |  | 
| 134 142 | 
             
                      # The resume token tracked by the change stream, used only
         | 
| 135 143 | 
             
                      # when there is no cursor, or no cursor resume token
         | 
| @@ -181,24 +189,30 @@ module Mongo | |
| 181 189 | 
             
                    # @return [ BSON::Document | nil ] A change stream document.
         | 
| 182 190 | 
             
                    # @since 2.6.0
         | 
| 183 191 | 
             
                    def try_next
         | 
| 192 | 
            +
                      recreate_cursor! if @timed_out
         | 
| 193 | 
            +
             | 
| 184 194 | 
             
                      raise StopIteration.new if closed?
         | 
| 195 | 
            +
             | 
| 185 196 | 
             
                      begin
         | 
| 186 197 | 
             
                        doc = @cursor.try_next
         | 
| 187 198 | 
             
                      rescue Mongo::Error => e
         | 
| 188 | 
            -
                         | 
| 189 | 
            -
             | 
| 190 | 
            -
                         | 
| 191 | 
            -
             | 
| 192 | 
            -
                        # | 
| 193 | 
            -
                        #  | 
| 194 | 
            -
                        #  | 
| 199 | 
            +
                        # "If a next call fails with a timeout error, drivers MUST NOT
         | 
| 200 | 
            +
                        # invalidate the change stream. The subsequent next call MUST
         | 
| 201 | 
            +
                        # perform a resume attempt to establish a new change stream on the
         | 
| 202 | 
            +
                        # server..."
         | 
| 203 | 
            +
                        #
         | 
| 204 | 
            +
                        # However, SocketTimeoutErrors are TimeoutErrors, but are also
         | 
| 205 | 
            +
                        # change-stream-resumable. To preserve existing (specified) behavior,
         | 
| 206 | 
            +
                        # We only count timeouts when the error is not also
         | 
| 207 | 
            +
                        # change-stream-resumable.
         | 
| 208 | 
            +
                        @timed_out = e.is_a?(Mongo::Error::TimeoutError) && !e.change_stream_resumable?
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                        raise unless @timed_out || e.change_stream_resumable?
         | 
| 195 211 |  | 
| 196 | 
            -
                        # Save cursor's resume token so we can use it
         | 
| 197 | 
            -
                        # to create a new cursor
         | 
| 198 212 | 
             
                        @resume_token = @cursor.resume_token
         | 
| 213 | 
            +
                        raise e if @timed_out
         | 
| 199 214 |  | 
| 200 | 
            -
                         | 
| 201 | 
            -
                        create_cursor!
         | 
| 215 | 
            +
                        recreate_cursor!(@cursor.context)
         | 
| 202 216 | 
             
                        retry
         | 
| 203 217 | 
             
                      end
         | 
| 204 218 |  | 
| @@ -231,14 +245,17 @@ module Mongo | |
| 231 245 | 
             
                    #   This method ignores any errors that occur when closing the
         | 
| 232 246 | 
             
                    #   server-side cursor.
         | 
| 233 247 | 
             
                    #
         | 
| 248 | 
            +
                    # @params [ Hash ] opts Options to be passed to the cursor close
         | 
| 249 | 
            +
                    #   command.
         | 
| 250 | 
            +
                    #
         | 
| 234 251 | 
             
                    # @return [ nil ] Always nil.
         | 
| 235 252 | 
             
                    #
         | 
| 236 253 | 
             
                    # @since 2.5.0
         | 
| 237 | 
            -
                    def close
         | 
| 254 | 
            +
                    def close(opts = {})
         | 
| 238 255 | 
             
                      unless closed?
         | 
| 239 256 | 
             
                        begin
         | 
| 240 | 
            -
                          @cursor.close
         | 
| 241 | 
            -
                        rescue Error::OperationFailure, Error::SocketError, Error::SocketTimeoutError, Error::MissingConnection
         | 
| 257 | 
            +
                          @cursor.close(opts)
         | 
| 258 | 
            +
                        rescue Error::OperationFailure::Family, Error::SocketError, Error::SocketTimeoutError, Error::MissingConnection
         | 
| 242 259 | 
             
                          # ignore
         | 
| 243 260 | 
             
                        end
         | 
| 244 261 | 
             
                        @cursor = nil
         | 
| @@ -284,6 +301,28 @@ module Mongo | |
| 284 301 | 
             
                      cursor_resume_token || @resume_token
         | 
| 285 302 | 
             
                    end
         | 
| 286 303 |  | 
| 304 | 
            +
                    # "change streams are an abstraction around tailable-awaitData cursors..."
         | 
| 305 | 
            +
                    #
         | 
| 306 | 
            +
                    # @return :tailable_await
         | 
| 307 | 
            +
                    def cursor_type
         | 
| 308 | 
            +
                      :tailable_await
         | 
| 309 | 
            +
                    end
         | 
| 310 | 
            +
             | 
| 311 | 
            +
                    # "change streams...implicitly use ITERATION mode"
         | 
| 312 | 
            +
                    #
         | 
| 313 | 
            +
                    # @return :iteration
         | 
| 314 | 
            +
                    def timeout_mode
         | 
| 315 | 
            +
                      :iteration
         | 
| 316 | 
            +
                    end
         | 
| 317 | 
            +
             | 
| 318 | 
            +
                    # Returns the value of the max_await_time_ms option that was
         | 
| 319 | 
            +
                    # passed to this change stream.
         | 
| 320 | 
            +
                    #
         | 
| 321 | 
            +
                    # @return [ Integer | nil ] the max_await_time_ms value
         | 
| 322 | 
            +
                    def max_await_time_ms
         | 
| 323 | 
            +
                      options[:max_await_time_ms]
         | 
| 324 | 
            +
                    end
         | 
| 325 | 
            +
             | 
| 287 326 | 
             
                    private
         | 
| 288 327 |  | 
| 289 328 | 
             
                    def for_cluster?
         | 
| @@ -298,19 +337,23 @@ module Mongo | |
| 298 337 | 
             
                      !for_cluster? && !for_database?
         | 
| 299 338 | 
             
                    end
         | 
| 300 339 |  | 
| 301 | 
            -
                    def create_cursor!
         | 
| 340 | 
            +
                    def create_cursor!(timeout_ms = nil)
         | 
| 302 341 | 
             
                      # clear the cache because we may get a newer or an older server
         | 
| 303 342 | 
             
                      # (rolling upgrades)
         | 
| 304 343 | 
             
                      @start_at_operation_time_supported = nil
         | 
| 305 344 |  | 
| 306 | 
            -
                      session = client. | 
| 345 | 
            +
                      session = client.get_session(@options)
         | 
| 346 | 
            +
                      context = Operation::Context.new(client: client, session: session, view: self, operation_timeouts: timeout_ms ? { operation_timeout_ms: timeout_ms } : operation_timeouts)
         | 
| 347 | 
            +
             | 
| 307 348 | 
             
                      start_at_operation_time = nil
         | 
| 308 349 | 
             
                      start_at_operation_time_supported = nil
         | 
| 309 | 
            -
             | 
| 350 | 
            +
             | 
| 351 | 
            +
                      @cursor = read_with_retry_cursor(session, server_selector, self, context: context) do |server|
         | 
| 310 352 | 
             
                        server.with_connection do |connection|
         | 
| 311 353 | 
             
                          start_at_operation_time_supported = connection.description.server_version_gte?('4.0')
         | 
| 312 354 |  | 
| 313 | 
            -
                          result = send_initial_query(connection,  | 
| 355 | 
            +
                          result = send_initial_query(connection, context)
         | 
| 356 | 
            +
             | 
| 314 357 | 
             
                          if doc = result.replies.first && result.replies.first.documents.first
         | 
| 315 358 | 
             
                            start_at_operation_time = doc['operationTime']
         | 
| 316 359 | 
             
                          else
         | 
| @@ -324,6 +367,7 @@ module Mongo | |
| 324 367 | 
             
                          result
         | 
| 325 368 | 
             
                        end
         | 
| 326 369 | 
             
                      end
         | 
| 370 | 
            +
             | 
| 327 371 | 
             
                      @start_at_operation_time = start_at_operation_time
         | 
| 328 372 | 
             
                      @start_at_operation_time_supported = start_at_operation_time_supported
         | 
| 329 373 | 
             
                    end
         | 
| @@ -390,11 +434,11 @@ module Mongo | |
| 390 434 | 
             
                      end
         | 
| 391 435 | 
             
                    end
         | 
| 392 436 |  | 
| 393 | 
            -
                    def send_initial_query(connection,  | 
| 394 | 
            -
                      initial_query_op(session, view.read_preference)
         | 
| 437 | 
            +
                    def send_initial_query(connection, context)
         | 
| 438 | 
            +
                      initial_query_op(context.session, view.read_preference)
         | 
| 395 439 | 
             
                        .execute_with_connection(
         | 
| 396 440 | 
             
                          connection,
         | 
| 397 | 
            -
                          context:  | 
| 441 | 
            +
                          context: context,
         | 
| 398 442 | 
             
                        )
         | 
| 399 443 | 
             
                    end
         | 
| 400 444 |  | 
| @@ -412,6 +456,15 @@ module Mongo | |
| 412 456 | 
             
                    def resuming?
         | 
| 413 457 | 
             
                      !!@resuming
         | 
| 414 458 | 
             
                    end
         | 
| 459 | 
            +
             | 
| 460 | 
            +
                    # Recreates the current cursor (typically as a consequence of attempting
         | 
| 461 | 
            +
                    # to resume the change stream)
         | 
| 462 | 
            +
                    def recreate_cursor!(context = nil)
         | 
| 463 | 
            +
                      @timed_out = false
         | 
| 464 | 
            +
             | 
| 465 | 
            +
                      close
         | 
| 466 | 
            +
                      create_cursor!(context&.remaining_timeout_ms)
         | 
| 467 | 
            +
                    end
         | 
| 415 468 | 
             
                  end
         | 
| 416 469 | 
             
                end
         | 
| 417 470 | 
             
              end
         |