mongo 2.6.0 → 2.6.1
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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +3 -1
- data/lib/mongo.rb +1 -0
- data/lib/mongo/address.rb +1 -1
- data/lib/mongo/address/ipv4.rb +1 -1
- data/lib/mongo/address/ipv6.rb +1 -1
- data/lib/mongo/address/unix.rb +1 -1
- data/lib/mongo/client.rb +14 -7
- data/lib/mongo/collection/view/builder/op_query.rb +9 -1
- data/lib/mongo/collection/view/readable.rb +7 -2
- data/lib/mongo/database.rb +2 -1
- data/lib/mongo/error.rb +1 -0
- data/lib/mongo/error/lint_error.rb +35 -0
- data/lib/mongo/lint.rb +45 -0
- data/lib/mongo/operation/shared/read_preference_supported.rb +2 -1
- data/lib/mongo/operation/shared/sessions_supported.rb +5 -7
- data/lib/mongo/protocol/query.rb +1 -0
- data/lib/mongo/server.rb +1 -1
- data/lib/mongo/server/connection_pool.rb +5 -5
- data/lib/mongo/server/connection_pool/queue.rb +4 -4
- data/lib/mongo/server_selector.rb +1 -0
- data/lib/mongo/session.rb +48 -17
- data/lib/mongo/socket.rb +67 -7
- data/lib/mongo/socket/ssl.rb +6 -0
- data/lib/mongo/version.rb +1 -1
- data/spec/mongo/lint_spec.rb +152 -0
- data/spec/mongo/logger_spec.rb +1 -1
- data/spec/mongo/socket/ssl_spec.rb +1 -1
- data/spec/mongo/socket_spec.rb +42 -0
- metadata +408 -402
- metadata.gz.sig +0 -0
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: d8e500892649b1d8d1817ec229c1d56d97837e252452654af04e995d1416082a
         | 
| 4 | 
            +
              data.tar.gz: 5fa8c7ca6caa84c0c81ed9f8ab3cd6566f579fc10ac28f510628f2de790040c7
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 725de14593b44d0bf8b370f53a5e18a8f5d033efddd2e05096ec561b69f51b01b70394d3c8ebab1e89dbb7ecbb7784dbb161376d1194130ae6d016afd39344ae
         | 
| 7 | 
            +
              data.tar.gz: 989ee85302acac376637d777e1c83cff4e7fa5d932fc62d489b3192d0b7336875fe831a0742d9953892108d2cb86c5b825f84d73a1779c0149fef2ffbb04aa05
         | 
    
        checksums.yaml.gz.sig
    CHANGED
    
    | Binary file | 
    
        data.tar.gz.sig
    CHANGED
    
    | @@ -1 +1,3 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            ���6Ș�����>in{��M�(����Jʐ4���E�tm}W�BA8�������y��z���W�S��^<~6���&%q�`��M��(����<�7��Kݰxj�����d�\����f���f�f��EML�|3�tK�DЂ9�J<3-�,���<�Q��!E�g>L>��8Yf95��֙<i��l�gu������m�x[=�f�
         | 
| 2 | 
            +
            ��;�|klr��n
         | 
| 3 | 
            +
            v�a����gn���0�O�I�S���r�PowVo`�
         | 
    
        data/lib/mongo.rb
    CHANGED
    
    
    
        data/lib/mongo/address.rb
    CHANGED
    
    | @@ -142,7 +142,7 @@ module Mongo | |
| 142 142 | 
             
                # @param [ Float ] socket_timeout The socket timeout.
         | 
| 143 143 | 
             
                # @param [ Hash ] ssl_options SSL options.
         | 
| 144 144 | 
             
                #
         | 
| 145 | 
            -
                # @return [  | 
| 145 | 
            +
                # @return [ Mongo::Socket::SSL, Mongo::Socket::TCP, Mongo::Socket::Unix ] The socket.
         | 
| 146 146 | 
             
                #
         | 
| 147 147 | 
             
                # @since 2.0.0
         | 
| 148 148 | 
             
                def socket(socket_timeout, ssl_options = {})
         | 
    
        data/lib/mongo/address/ipv4.rb
    CHANGED
    
    | @@ -80,7 +80,7 @@ module Mongo | |
| 80 80 | 
             
                  # @param [ Float ] socket_timeout The socket timeout.
         | 
| 81 81 | 
             
                  # @param [ Hash ] ssl_options SSL options.
         | 
| 82 82 | 
             
                  #
         | 
| 83 | 
            -
                  # @return [  | 
| 83 | 
            +
                  # @return [ Mongo::Socket::SSL, Mongo::Socket::TCP ] The socket.
         | 
| 84 84 | 
             
                  #
         | 
| 85 85 | 
             
                  # @since 2.0.0
         | 
| 86 86 | 
             
                  def socket(socket_timeout, ssl_options = {})
         | 
    
        data/lib/mongo/address/ipv6.rb
    CHANGED
    
    | @@ -96,7 +96,7 @@ module Mongo | |
| 96 96 | 
             
                  # @param [ Float ] socket_timeout The socket timeout.
         | 
| 97 97 | 
             
                  # @param [ Hash ] ssl_options SSL options.
         | 
| 98 98 | 
             
                  #
         | 
| 99 | 
            -
                  # @return [  | 
| 99 | 
            +
                  # @return [ Mongo::Socket::SSL, Mongo::Socket::TCP ] The socket.
         | 
| 100 100 | 
             
                  #
         | 
| 101 101 | 
             
                  # @since 2.0.0
         | 
| 102 102 | 
             
                  def socket(socket_timeout, ssl_options = {})
         | 
    
        data/lib/mongo/address/unix.rb
    CHANGED
    
    | @@ -65,7 +65,7 @@ module Mongo | |
| 65 65 | 
             
                  # @param [ Float ] socket_timeout The socket timeout.
         | 
| 66 66 | 
             
                  # @param [ Hash ] ssl_options SSL options - ignored.
         | 
| 67 67 | 
             
                  #
         | 
| 68 | 
            -
                  # @return [  | 
| 68 | 
            +
                  # @return [ Mongo::Socket::Unix ] The socket.
         | 
| 69 69 | 
             
                  #
         | 
| 70 70 | 
             
                  # @since 2.0.0
         | 
| 71 71 | 
             
                  def socket(socket_timeout, ssl_options = {})
         | 
    
        data/lib/mongo/client.rb
    CHANGED
    
    | @@ -193,7 +193,7 @@ module Mongo | |
| 193 193 | 
             
                #   supports 'zlib'.
         | 
| 194 194 | 
             
                # @option options [ Hash ] :read The read preference options. The hash
         | 
| 195 195 | 
             
                #   may have the following items:
         | 
| 196 | 
            -
                #   - *: | 
| 196 | 
            +
                #   - *:mode* -- read preference specified as a symbol; valid values are
         | 
| 197 197 | 
             
                #     *:primary*, *:primary_preferred*, *:secondary*, *:secondary_preferred*
         | 
| 198 198 | 
             
                #     and *:nearest*.
         | 
| 199 199 | 
             
                #   - *:tag_sets* -- an array of hashes.
         | 
| @@ -219,18 +219,18 @@ module Mongo | |
| 219 219 | 
             
                # @option options [ OpenSSL::PKey ] :ssl_key_object The private key used to identify the
         | 
| 220 220 | 
             
                #   connection against MongoDB
         | 
| 221 221 | 
             
                # @option options [ String ] :ssl_key_pass_phrase A passphrase for the private key.
         | 
| 222 | 
            -
                # @option options [ true, false ] :ssl_verify Whether  | 
| 222 | 
            +
                # @option options [ true, false ] :ssl_verify Whether to do peer certificate
         | 
| 223 223 | 
             
                #   validation.
         | 
| 224 | 
            -
                # @option options [ String ] :ssl_ca_cert The file containing  | 
| 225 | 
            -
                #    | 
| 224 | 
            +
                # @option options [ String ] :ssl_ca_cert The file containing concatenated
         | 
| 225 | 
            +
                #   certificate authority certificates used to validate certs passed from the
         | 
| 226 226 | 
             
                #   other end of the connection. One of :ssl_ca_cert, :ssl_ca_cert_string or
         | 
| 227 227 | 
             
                #   :ssl_ca_cert_object (in order of priority) is required for :ssl_verify.
         | 
| 228 | 
            -
                # @option options [ String ] :ssl_ca_cert_string A string containing  | 
| 229 | 
            -
                #    | 
| 228 | 
            +
                # @option options [ String ] :ssl_ca_cert_string A string containing concatenated
         | 
| 229 | 
            +
                #   certificate authority certificates used to validate certs passed from the
         | 
| 230 230 | 
             
                #   other end of the connection. One of :ssl_ca_cert, :ssl_ca_cert_string or
         | 
| 231 231 | 
             
                #   :ssl_ca_cert_object (in order of priority) is required for :ssl_verify.
         | 
| 232 232 | 
             
                # @option options [ Array<OpenSSL::X509::Certificate> ] :ssl_ca_cert_object An array of
         | 
| 233 | 
            -
                #   OpenSSL::X509::Certificate representing the  | 
| 233 | 
            +
                #   OpenSSL::X509::Certificate representing the certificate authority certificates used
         | 
| 234 234 | 
             
                #   to validate certs passed from the other end of the connection. One of :ssl_ca_cert,
         | 
| 235 235 | 
             
                #   :ssl_ca_cert_string or :ssl_ca_cert_object (in order of priority) is required for :ssl_verify.
         | 
| 236 236 | 
             
                # @option options [ Float ] :socket_timeout The timeout, in seconds, to
         | 
| @@ -261,6 +261,7 @@ module Mongo | |
| 261 261 | 
             
                #
         | 
| 262 262 | 
             
                # @since 2.0.0
         | 
| 263 263 | 
             
                def initialize(addresses_or_uri, options = Options::Redacted.new)
         | 
| 264 | 
            +
                  Mongo::Lint.validate_underscore_read_preference(options[:read])
         | 
| 264 265 | 
             
                  if addresses_or_uri.is_a?(::String)
         | 
| 265 266 | 
             
                    create_from_uri(addresses_or_uri, validate_options!(options))
         | 
| 266 267 | 
             
                  else
         | 
| @@ -301,6 +302,12 @@ module Mongo | |
| 301 302 | 
             
                #   client.read_preference
         | 
| 302 303 | 
             
                #
         | 
| 303 304 | 
             
                # @return [ BSON::Document ] The user-defined read preference.
         | 
| 305 | 
            +
                #   The document may have the following fields:
         | 
| 306 | 
            +
                #   - *:read* -- read preference specified as a symbol; valid values are
         | 
| 307 | 
            +
                #     *:primary*, *:primary_preferred*, *:secondary*, *:secondary_preferred*
         | 
| 308 | 
            +
                #     and *:nearest*.
         | 
| 309 | 
            +
                #   - *:tag_sets* -- an array of hashes.
         | 
| 310 | 
            +
                #   - *:local_threshold*.
         | 
| 304 311 | 
             
                #
         | 
| 305 312 | 
             
                # @since 2.0.0
         | 
| 306 313 | 
             
                def read_preference
         | 
| @@ -68,7 +68,15 @@ module Mongo | |
| 68 68 | 
             
                      end
         | 
| 69 69 |  | 
| 70 70 | 
             
                      def read_pref_formatted
         | 
| 71 | 
            -
                        @read_formatted ||=  | 
| 71 | 
            +
                        @read_formatted ||= begin
         | 
| 72 | 
            +
                          if read
         | 
| 73 | 
            +
                            read_pref = ServerSelector.get(read).to_mongos
         | 
| 74 | 
            +
                            Mongo::Lint.validate_camel_case_read_preference(read_pref)
         | 
| 75 | 
            +
                            read_pref
         | 
| 76 | 
            +
                          else
         | 
| 77 | 
            +
                            nil
         | 
| 78 | 
            +
                          end
         | 
| 79 | 
            +
                        end
         | 
| 72 80 | 
             
                      end
         | 
| 73 81 |  | 
| 74 82 | 
             
                      def special_filter
         | 
| @@ -139,6 +139,7 @@ module Mongo | |
| 139 139 | 
             
                      cmd[:limit] = opts[:limit] if opts[:limit]
         | 
| 140 140 | 
             
                      cmd[:maxTimeMS] = opts[:max_time_ms] if opts[:max_time_ms]
         | 
| 141 141 | 
             
                      cmd[:readConcern] = collection.read_concern if collection.read_concern
         | 
| 142 | 
            +
                      Mongo::Lint.validate_underscore_read_preference(opts[:read])
         | 
| 142 143 | 
             
                      read_pref = opts[:read] || read_preference
         | 
| 143 144 | 
             
                      selector = ServerSelector.get(read_pref || server_selector)
         | 
| 144 145 | 
             
                      with_session(opts) do |session|
         | 
| @@ -203,6 +204,7 @@ module Mongo | |
| 203 204 | 
             
                      cmd = { count: collection.name }
         | 
| 204 205 | 
             
                      cmd[:maxTimeMS] = opts[:max_time_ms] if opts[:max_time_ms]
         | 
| 205 206 | 
             
                      cmd[:readConcern] = collection.read_concern if collection.read_concern
         | 
| 207 | 
            +
                      Mongo::Lint.validate_underscore_read_preference(opts[:read])
         | 
| 206 208 | 
             
                      read_pref = opts[:read] || read_preference
         | 
| 207 209 | 
             
                      selector = ServerSelector.get(read_pref || server_selector)
         | 
| 208 210 | 
             
                      with_session(opts) do |session|
         | 
| @@ -240,6 +242,7 @@ module Mongo | |
| 240 242 | 
             
                              :query => filter }
         | 
| 241 243 | 
             
                      cmd[:maxTimeMS] = opts[:max_time_ms] if opts[:max_time_ms]
         | 
| 242 244 | 
             
                      cmd[:readConcern] = collection.read_concern if collection.read_concern
         | 
| 245 | 
            +
                      Mongo::Lint.validate_underscore_read_preference(opts[:read])
         | 
| 243 246 | 
             
                      read_pref = opts[:read] || read_preference
         | 
| 244 247 | 
             
                      selector = ServerSelector.get(read_pref || server_selector)
         | 
| 245 248 | 
             
                      with_session(opts) do |session|
         | 
| @@ -537,11 +540,13 @@ module Mongo | |
| 537 540 | 
             
                    end
         | 
| 538 541 |  | 
| 539 542 | 
             
                    def read_preference
         | 
| 540 | 
            -
                      if options[:session] && options[:session].in_transaction?
         | 
| 541 | 
            -
                        options[:session]. | 
| 543 | 
            +
                      rp = if options[:session] && options[:session].in_transaction?
         | 
| 544 | 
            +
                        options[:session].txn_read_preference || collection.client.read_preference
         | 
| 542 545 | 
             
                      else
         | 
| 543 546 | 
             
                        @read_preference ||= (options[:read] || collection.read_preference)
         | 
| 544 547 | 
             
                      end
         | 
| 548 | 
            +
                      Mongo::Lint.validate_underscore_read_preference(rp)
         | 
| 549 | 
            +
                      rp
         | 
| 545 550 | 
             
                    end
         | 
| 546 551 |  | 
| 547 552 | 
             
                    def server_selector
         | 
    
        data/lib/mongo/database.rb
    CHANGED
    
    | @@ -153,7 +153,8 @@ module Mongo | |
| 153 153 | 
             
                #
         | 
| 154 154 | 
             
                # @return [ Hash ] The result of the command execution.
         | 
| 155 155 | 
             
                def command(operation, opts = {})
         | 
| 156 | 
            -
                  txn_read_pref = opts[:session] && opts[:session].in_transaction? && opts[:session]. | 
| 156 | 
            +
                  txn_read_pref = opts[:session] && opts[:session].in_transaction? && opts[:session].txn_read_preference
         | 
| 157 | 
            +
                  Mongo::Lint.validate_underscore_read_preference(txn_read_pref)
         | 
| 157 158 | 
             
                  preference = ServerSelector.get(txn_read_pref || opts[:read] || ServerSelector::PRIMARY)
         | 
| 158 159 | 
             
                  server = preference.select_server(cluster)
         | 
| 159 160 |  | 
    
        data/lib/mongo/error.rb
    CHANGED
    
    | @@ -151,6 +151,7 @@ require 'mongo/error/invalid_update_document' | |
| 151 151 | 
             
            require 'mongo/error/invalid_uri'
         | 
| 152 152 | 
             
            require 'mongo/error/invalid_write_concern'
         | 
| 153 153 | 
             
            require 'mongo/error/insufficient_iteration_count'
         | 
| 154 | 
            +
            require 'mongo/error/lint_error'
         | 
| 154 155 | 
             
            require 'mongo/error/max_bson_size'
         | 
| 155 156 | 
             
            require 'mongo/error/max_message_size'
         | 
| 156 157 | 
             
            require 'mongo/error/mismatched_domain'
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            # Copyright (C) 2018 MongoDB, Inc.
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            # Licensed under the Apache License, Version 2.0 (the "License");
         | 
| 4 | 
            +
            # you may not use this file except in compliance with the License.
         | 
| 5 | 
            +
            # You may obtain a copy of the License at
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            #   http://www.apache.org/licenses/LICENSE-2.0
         | 
| 8 | 
            +
            #
         | 
| 9 | 
            +
            # Unless required by applicable law or agreed to in writing, software
         | 
| 10 | 
            +
            # distributed under the License is distributed on an "AS IS" BASIS,
         | 
| 11 | 
            +
            # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         | 
| 12 | 
            +
            # See the License for the specific language governing permissions and
         | 
| 13 | 
            +
            # limitations under the License.
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            module Mongo
         | 
| 16 | 
            +
              class Error
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                # Raised when the driver is used incorrectly.
         | 
| 19 | 
            +
                #
         | 
| 20 | 
            +
                # Normally the driver passes certain data to the server and lets the
         | 
| 21 | 
            +
                # server return an error if the data is invalid. This makes it possible
         | 
| 22 | 
            +
                # for the server to add functionality in the future and for older
         | 
| 23 | 
            +
                # driver versions to support such functionality transparently, but
         | 
| 24 | 
            +
                # also complicates debugging.
         | 
| 25 | 
            +
                #
         | 
| 26 | 
            +
                # Setting the environment variable MONGO_RUBY_DRIVER_LINT to 1, true
         | 
| 27 | 
            +
                # or yes will make the driver perform additional checks on data it passes
         | 
| 28 | 
            +
                # to the server, to flag failures sooner. This exception is raised on
         | 
| 29 | 
            +
                # such failures.
         | 
| 30 | 
            +
                #
         | 
| 31 | 
            +
                # @since 2.7.0
         | 
| 32 | 
            +
                class LintError < Error
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
    
        data/lib/mongo/lint.rb
    ADDED
    
    | @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            module Mongo
         | 
| 2 | 
            +
              # @api private
         | 
| 3 | 
            +
              module Lint
         | 
| 4 | 
            +
                def validate_underscore_read_preference(read_pref)
         | 
| 5 | 
            +
                  return unless enabled?
         | 
| 6 | 
            +
                  if read_pref
         | 
| 7 | 
            +
                    validate_underscore_read_preference_mode(read_pref[:mode] || read_pref['mode'])
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
                module_function :validate_underscore_read_preference
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def validate_underscore_read_preference_mode(mode)
         | 
| 13 | 
            +
                  return unless enabled?
         | 
| 14 | 
            +
                  if mode
         | 
| 15 | 
            +
                    unless %w(primary primary_preferred secondary secondary_preferred nearest).include?(mode.to_s)
         | 
| 16 | 
            +
                      raise Error::LintError, "Invalid read preference mode: #{mode}"
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
                module_function :validate_underscore_read_preference_mode
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def validate_camel_case_read_preference(read_pref)
         | 
| 23 | 
            +
                  return unless enabled?
         | 
| 24 | 
            +
                  if read_pref
         | 
| 25 | 
            +
                    validate_camel_case_read_preference_mode(read_pref[:mode] || read_pref['mode'])
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
                module_function :validate_camel_case_read_preference
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def validate_camel_case_read_preference_mode(mode)
         | 
| 31 | 
            +
                  return unless enabled?
         | 
| 32 | 
            +
                  if mode
         | 
| 33 | 
            +
                    unless %w(primary primaryPreferred secondary secondaryPreferred nearest).include?(mode.to_s)
         | 
| 34 | 
            +
                      raise Error::LintError, "Invalid read preference mode: #{mode}"
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
                module_function :validate_camel_case_read_preference_mode
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                def enabled?
         | 
| 41 | 
            +
                  ENV['MONGO_RUBY_DRIVER_LINT'] && %w(1 yes true).include?(ENV['MONGO_RUBY_DRIVER_LINT'].downcase)
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
                module_function :enabled?
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
            end
         | 
| @@ -30,8 +30,9 @@ module Mongo | |
| 30 30 |  | 
| 31 31 | 
             
                  def update_selector_for_read_pref(sel, server)
         | 
| 32 32 | 
             
                    if read && server.mongos? && read_pref = read.to_mongos
         | 
| 33 | 
            +
                      Mongo::Lint.validate_camel_case_read_preference(read_pref)
         | 
| 33 34 | 
             
                      sel = sel[:$query] ? sel : {:$query => sel}
         | 
| 34 | 
            -
                      sel.merge(:$readPreference => read_pref)
         | 
| 35 | 
            +
                      sel = sel.merge(:$readPreference => read_pref)
         | 
| 35 36 | 
             
                    else
         | 
| 36 37 | 
             
                      sel
         | 
| 37 38 | 
             
                    end
         | 
| @@ -24,8 +24,6 @@ module Mongo | |
| 24 24 |  | 
| 25 25 | 
             
                  ZERO_TIMESTAMP = BSON::Timestamp.new(0, 0)
         | 
| 26 26 |  | 
| 27 | 
            -
                  READ_PREFERENCE = '$readPreference'.freeze
         | 
| 28 | 
            -
             | 
| 29 27 | 
             
                  READ_COMMANDS = [
         | 
| 30 28 | 
             
                    :aggregate,
         | 
| 31 29 | 
             
                    :collStats,
         | 
| @@ -101,8 +99,8 @@ module Mongo | |
| 101 99 | 
             
                    session.suppress_read_write_concern!(selector)
         | 
| 102 100 | 
             
                  end
         | 
| 103 101 |  | 
| 104 | 
            -
                  def  | 
| 105 | 
            -
                    session. | 
| 102 | 
            +
                  def validate_read_preference!(selector)
         | 
| 103 | 
            +
                    session.validate_read_preference!(selector) if read_command?(selector)
         | 
| 106 104 | 
             
                  end
         | 
| 107 105 |  | 
| 108 106 | 
             
                  def update_session_state!
         | 
| @@ -113,7 +111,7 @@ module Mongo | |
| 113 111 | 
             
                    sel = selector(server)
         | 
| 114 112 | 
             
                    add_write_concern!(sel)
         | 
| 115 113 | 
             
                    sel[Protocol::Msg::DATABASE_IDENTIFIER] = db_name
         | 
| 116 | 
            -
                    sel[ | 
| 114 | 
            +
                    sel['$readPreference'] = read.to_doc if read
         | 
| 117 115 |  | 
| 118 116 | 
             
                    if server.features.sessions_enabled?
         | 
| 119 117 | 
             
                      apply_cluster_time!(sel, server)
         | 
| @@ -125,7 +123,7 @@ module Mongo | |
| 125 123 | 
             
                        apply_autocommit!(sel)
         | 
| 126 124 | 
             
                        apply_txn_opts!(sel)
         | 
| 127 125 | 
             
                        suppress_read_write_concern!(sel)
         | 
| 128 | 
            -
                         | 
| 126 | 
            +
                        validate_read_preference!(sel)
         | 
| 129 127 | 
             
                        update_session_state!
         | 
| 130 128 | 
             
                        apply_txn_num!(sel)
         | 
| 131 129 | 
             
                      end
         | 
| @@ -138,7 +136,7 @@ module Mongo | |
| 138 136 | 
             
                      apply_autocommit!(sel)
         | 
| 139 137 | 
             
                      apply_txn_opts!(sel)
         | 
| 140 138 | 
             
                      suppress_read_write_concern!(sel)
         | 
| 141 | 
            -
                       | 
| 139 | 
            +
                      validate_read_preference!(sel)
         | 
| 142 140 | 
             
                      update_session_state!
         | 
| 143 141 | 
             
                      apply_txn_num!(sel)
         | 
| 144 142 | 
             
                    end
         | 
    
        data/lib/mongo/protocol/query.rb
    CHANGED
    
    | @@ -301,6 +301,7 @@ module Mongo | |
| 301 301 | 
             
                      OPTION_MAPPINGS.each do |legacy, option|
         | 
| 302 302 | 
             
                        document.store(option, options[legacy]) unless options[legacy].nil?
         | 
| 303 303 | 
             
                      end
         | 
| 304 | 
            +
                      Mongo::Lint.validate_camel_case_read_preference(filter['readPreference'])
         | 
| 304 305 | 
             
                      SPECIAL_FIELD_MAPPINGS.each do |special, normal|
         | 
| 305 306 | 
             
                        document.store(normal, filter[special]) unless filter[special].nil?
         | 
| 306 307 | 
             
                      end
         | 
    
        data/lib/mongo/server.rb
    CHANGED
    
    
| @@ -47,7 +47,7 @@ module Mongo | |
| 47 47 | 
             
                  # @example Check a connection out from the pool.
         | 
| 48 48 | 
             
                  #   pool.checkout
         | 
| 49 49 | 
             
                  #
         | 
| 50 | 
            -
                  # @return [ Mongo:: | 
| 50 | 
            +
                  # @return [ Mongo::Server::Connection ] The checked out connection.
         | 
| 51 51 | 
             
                  #
         | 
| 52 52 | 
             
                  # @since 2.0.0
         | 
| 53 53 | 
             
                  def checkout
         | 
| @@ -120,14 +120,14 @@ module Mongo | |
| 120 120 |  | 
| 121 121 | 
             
                  class << self
         | 
| 122 122 |  | 
| 123 | 
            -
                    #  | 
| 123 | 
            +
                    # Creates a new connection pool for the provided server.
         | 
| 124 124 | 
             
                    #
         | 
| 125 | 
            -
                    # @example  | 
| 126 | 
            -
                    #   Mongo:: | 
| 125 | 
            +
                    # @example Create a new connection pool.
         | 
| 126 | 
            +
                    #   Mongo::Server::ConnectionPool.get(server)
         | 
| 127 127 | 
             
                    #
         | 
| 128 128 | 
             
                    # @param [ Mongo::Server ] server The server.
         | 
| 129 129 | 
             
                    #
         | 
| 130 | 
            -
                    # @return [ Mongo:: | 
| 130 | 
            +
                    # @return [ Mongo::Server::ConnectionPool ] The connection pool.
         | 
| 131 131 | 
             
                    #
         | 
| 132 132 | 
             
                    # @since 2.0.0
         | 
| 133 133 | 
             
                    def get(server)
         | 
| @@ -53,7 +53,7 @@ module Mongo | |
| 53 53 | 
             
                    # @example Dequeue a connection.
         | 
| 54 54 | 
             
                    #   queue.dequeue
         | 
| 55 55 | 
             
                    #
         | 
| 56 | 
            -
                    # @return [ Mongo:: | 
| 56 | 
            +
                    # @return [ Mongo::Server::Connection ] The next connection.
         | 
| 57 57 | 
             
                    #
         | 
| 58 58 | 
             
                    # @since 2.0.0
         | 
| 59 59 | 
             
                    def dequeue
         | 
| @@ -82,7 +82,7 @@ module Mongo | |
| 82 82 | 
             
                    # @example Enqueue a connection.
         | 
| 83 83 | 
             
                    #   queue.enqueue(connection)
         | 
| 84 84 | 
             
                    #
         | 
| 85 | 
            -
                    # @param [ Mongo:: | 
| 85 | 
            +
                    # @param [ Mongo::Server::Connection ] connection The connection.
         | 
| 86 86 | 
             
                    #
         | 
| 87 87 | 
             
                    # @since 2.0.0
         | 
| 88 88 | 
             
                    def enqueue(connection)
         | 
| @@ -96,7 +96,7 @@ module Mongo | |
| 96 96 | 
             
                    # the initial size of the queue.
         | 
| 97 97 | 
             
                    #
         | 
| 98 98 | 
             
                    # @example Create the queue.
         | 
| 99 | 
            -
                    #   Mongo:: | 
| 99 | 
            +
                    #   Mongo::Server::ConnectionPool::Queue.new(max_pool_size: 5) { Connection.new }
         | 
| 100 100 | 
             
                    #
         | 
| 101 101 | 
             
                    # @param [ Hash ] options The options.
         | 
| 102 102 | 
             
                    #
         | 
| @@ -124,7 +124,7 @@ module Mongo | |
| 124 124 | 
             
                    #
         | 
| 125 125 | 
             
                    # @since 2.0.0
         | 
| 126 126 | 
             
                    def inspect
         | 
| 127 | 
            -
                      "#<Mongo:: | 
| 127 | 
            +
                      "#<Mongo::Server::ConnectionPool::Queue:0x#{object_id} min_size=#{min_size} max_size=#{max_size} " +
         | 
| 128 128 | 
             
                        "wait_timeout=#{wait_timeout} current_size=#{queue.size}>"
         | 
| 129 129 | 
             
                    end
         | 
| 130 130 |  | 
| @@ -71,6 +71,7 @@ module Mongo | |
| 71 71 | 
             
                # @since 2.0.0
         | 
| 72 72 | 
             
                def get(preference = {})
         | 
| 73 73 | 
             
                  return preference if PREFERENCES.values.include?(preference.class)
         | 
| 74 | 
            +
                  Mongo::Lint.validate_underscore_read_preference(preference)
         | 
| 74 75 | 
             
                  PREFERENCES.fetch((preference[:mode] || :primary).to_sym).new(preference)
         | 
| 75 76 | 
             
                end
         | 
| 76 77 | 
             
              end
         | 
    
        data/lib/mongo/session.rb
    CHANGED
    
    | @@ -110,9 +110,16 @@ module Mongo | |
| 110 110 | 
             
                # @param [ Client ] client The client through which this session is created.
         | 
| 111 111 | 
             
                # @param [ Hash ] options The options for this session.
         | 
| 112 112 | 
             
                #
         | 
| 113 | 
            +
                # @option options [ Hash ] :read_preference The read preference options hash,
         | 
| 114 | 
            +
                #   with the following optional keys:
         | 
| 115 | 
            +
                #   - *:mode* -- the read preference as a string or symbol; valid values are
         | 
| 116 | 
            +
                #     *:primary*, *:primary_preferred*, *:secondary*, *:secondary_preferred*
         | 
| 117 | 
            +
                #     and *:nearest*.
         | 
| 118 | 
            +
                #
         | 
| 113 119 | 
             
                # @since 2.5.0
         | 
| 114 120 | 
             
                def initialize(server_session, client, options = {})
         | 
| 115 121 | 
             
                  @server_session = server_session
         | 
| 122 | 
            +
                  options = options.dup
         | 
| 116 123 |  | 
| 117 124 | 
             
                  # Because the read preference will need to be inserted into a command as a string, we convert
         | 
| 118 125 | 
             
                  # it from a symbol immediately upon receiving it.
         | 
| @@ -121,7 +128,7 @@ module Mongo | |
| 121 128 | 
             
                  end
         | 
| 122 129 |  | 
| 123 130 | 
             
                  @client = client.use(:admin)
         | 
| 124 | 
            -
                  @options = options. | 
| 131 | 
            +
                  @options = options.freeze
         | 
| 125 132 | 
             
                  @cluster_time = nil
         | 
| 126 133 | 
             
                  @state = NO_TRANSACTION_STATE
         | 
| 127 134 | 
             
                end
         | 
| @@ -232,7 +239,13 @@ module Mongo | |
| 232 239 | 
             
                def add_txn_opts!(command, read)
         | 
| 233 240 | 
             
                  command.tap do |c|
         | 
| 234 241 | 
             
                    # The read preference should be added for all read operations.
         | 
| 235 | 
            -
                     | 
| 242 | 
            +
                    if read && txn_read_pref = txn_read_preference
         | 
| 243 | 
            +
                      Mongo::Lint.validate_underscore_read_preference(txn_read_pref)
         | 
| 244 | 
            +
                      txn_read_pref = txn_read_pref.dup
         | 
| 245 | 
            +
                      txn_read_pref[:mode] = txn_read_pref[:mode].to_s.gsub(/(_\w)/) { |match| match[1].upcase }
         | 
| 246 | 
            +
                      Mongo::Lint.validate_camel_case_read_preference(txn_read_pref)
         | 
| 247 | 
            +
                      c['$readPreference'] = txn_read_pref
         | 
| 248 | 
            +
                    end
         | 
| 236 249 |  | 
| 237 250 | 
             
                    # The read concern should be added to any command that starts a transaction.
         | 
| 238 251 | 
             
                    if starting_transaction? && txn_read_concern
         | 
| @@ -277,14 +290,14 @@ module Mongo | |
| 277 290 | 
             
                # Ensure that the read preference of a command primary.
         | 
| 278 291 | 
             
                #
         | 
| 279 292 | 
             
                # @example
         | 
| 280 | 
            -
                #   session. | 
| 293 | 
            +
                #   session.validate_read_preference!(command)
         | 
| 281 294 | 
             
                #
         | 
| 282 295 | 
             
                # @raise [ Mongo::Error::InvalidTransactionOperation ] If the read preference of the command is
         | 
| 283 296 | 
             
                # not primary.
         | 
| 284 297 | 
             
                #
         | 
| 285 298 | 
             
                # @since 2.6.0
         | 
| 286 | 
            -
                def  | 
| 287 | 
            -
                  return unless in_transaction? &&  | 
| 299 | 
            +
                def validate_read_preference!(command)
         | 
| 300 | 
            +
                  return unless in_transaction? && non_primary_read_preference_mode?(command)
         | 
| 288 301 |  | 
| 289 302 | 
             
                  raise Mongo::Error::InvalidTransactionOperation.new(
         | 
| 290 303 | 
             
                    Mongo::Error::InvalidTransactionOperation::INVALID_READ_PREFERENCE)
         | 
| @@ -452,14 +465,29 @@ module Mongo | |
| 452 465 |  | 
| 453 466 | 
             
                # Start a new transaction.
         | 
| 454 467 | 
             
                #
         | 
| 455 | 
            -
                # Note that the transaction will not be started on the server until an | 
| 456 | 
            -
                # after start_transaction is called.
         | 
| 468 | 
            +
                # Note that the transaction will not be started on the server until an
         | 
| 469 | 
            +
                # operation is performed after start_transaction is called.
         | 
| 457 470 | 
             
                #
         | 
| 458 471 | 
             
                # @example Start a new transaction
         | 
| 459 472 | 
             
                #   session.start_transaction(options)
         | 
| 460 473 | 
             
                #
         | 
| 461 | 
            -
                # @ | 
| 462 | 
            -
                # | 
| 474 | 
            +
                # @param [ Hash ] options The options for the transaction being started.
         | 
| 475 | 
            +
                #
         | 
| 476 | 
            +
                # @option options [ Hash ] read_concern The read concern options hash, with the following
         | 
| 477 | 
            +
                #   optional keys:
         | 
| 478 | 
            +
                #   - *:level* -- the read preference level as a symbol; valid values are *:local*, *:majority*,
         | 
| 479 | 
            +
                #   and *:snapshot*
         | 
| 480 | 
            +
                #   - *:after_cluster_time* -- the cluster time BSON::Document or hash specifying which cluster
         | 
| 481 | 
            +
                #   time reads should occur after
         | 
| 482 | 
            +
                # @option options [ Hash ] :write_concern The write concern options. Can be :w =>
         | 
| 483 | 
            +
                #   Integer|String, :fsync => Boolean, :j => Boolean.
         | 
| 484 | 
            +
                # @option options [ Hash ] :read The read preference options. The hash may have the following
         | 
| 485 | 
            +
                #   items:
         | 
| 486 | 
            +
                #   - *:mode* -- read preference specified as a symbol; the only valid value is
         | 
| 487 | 
            +
                #     *:primary*.
         | 
| 488 | 
            +
                #
         | 
| 489 | 
            +
                # @raise [ InvalidTransactionOperation ] If a transaction is already in
         | 
| 490 | 
            +
                # progress or if the write concern is unacknowledged.
         | 
| 463 491 | 
             
                #
         | 
| 464 492 | 
             
                # @since 2.6.0
         | 
| 465 493 | 
             
                def start_transaction(options = nil)
         | 
| @@ -594,18 +622,21 @@ module Mongo | |
| 594 622 | 
             
                  within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE)
         | 
| 595 623 | 
             
                end
         | 
| 596 624 |  | 
| 597 | 
            -
                # Get the read preference  | 
| 625 | 
            +
                # Get the read preference the session will use in the currently
         | 
| 626 | 
            +
                # active transaction.
         | 
| 627 | 
            +
                #
         | 
| 628 | 
            +
                # This is a driver style hash with underscore keys.
         | 
| 598 629 | 
             
                #
         | 
| 599 630 | 
             
                # @example Get the transaction's read preference
         | 
| 600 | 
            -
                #   session. | 
| 631 | 
            +
                #   session.txn_read_preference
         | 
| 601 632 | 
             
                #
         | 
| 602 | 
            -
                # @return [ Hash ] The read preference  | 
| 633 | 
            +
                # @return [ Hash ] The read preference of the transaction.
         | 
| 603 634 | 
             
                #
         | 
| 604 635 | 
             
                # @since 2.6.0
         | 
| 605 | 
            -
                def  | 
| 606 | 
            -
                  rp =  | 
| 607 | 
            -
                     | 
| 608 | 
            -
                   | 
| 636 | 
            +
                def txn_read_preference
         | 
| 637 | 
            +
                  rp = txn_options && txn_options[:read_preference] ||
         | 
| 638 | 
            +
                    @client.read_preference
         | 
| 639 | 
            +
                  Mongo::Lint.validate_underscore_read_preference(rp)
         | 
| 609 640 | 
             
                  rp
         | 
| 610 641 | 
             
                end
         | 
| 611 642 |  | 
| @@ -639,7 +670,7 @@ module Mongo | |
| 639 670 | 
             
                    (@client.write_concern && @client.write_concern.options)
         | 
| 640 671 | 
             
                end
         | 
| 641 672 |  | 
| 642 | 
            -
                def  | 
| 673 | 
            +
                def non_primary_read_preference_mode?(command)
         | 
| 643 674 | 
             
                  return false unless command['$readPreference']
         | 
| 644 675 |  | 
| 645 676 | 
             
                  mode = command['$readPreference']['mode'] || command['$readPreference'][:mode]
         |