mongo 2.13.0.beta1 → 2.13.0.rc1

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.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -5
  4. data/Rakefile +15 -9
  5. data/lib/mongo.rb +4 -2
  6. data/lib/mongo/auth/aws/request.rb +4 -2
  7. data/lib/mongo/bulk_write.rb +1 -0
  8. data/lib/mongo/client.rb +143 -21
  9. data/lib/mongo/cluster.rb +53 -17
  10. data/lib/mongo/cluster/sdam_flow.rb +13 -10
  11. data/lib/mongo/cluster/topology/replica_set_no_primary.rb +3 -2
  12. data/lib/mongo/cluster/topology/sharded.rb +1 -1
  13. data/lib/mongo/cluster/topology/single.rb +1 -1
  14. data/lib/mongo/collection.rb +17 -13
  15. data/lib/mongo/collection/view/readable.rb +3 -1
  16. data/lib/mongo/collection/view/writable.rb +41 -5
  17. data/lib/mongo/database.rb +31 -4
  18. data/lib/mongo/database/view.rb +19 -4
  19. data/lib/mongo/distinguishing_semaphore.rb +55 -0
  20. data/lib/mongo/error.rb +1 -0
  21. data/lib/mongo/error/invalid_session.rb +2 -1
  22. data/lib/mongo/error/operation_failure.rb +6 -0
  23. data/lib/mongo/error/sessions_not_supported.rb +35 -0
  24. data/lib/mongo/event/base.rb +6 -0
  25. data/lib/mongo/grid/file.rb +5 -0
  26. data/lib/mongo/grid/file/chunk.rb +2 -0
  27. data/lib/mongo/grid/fs_bucket.rb +15 -13
  28. data/lib/mongo/grid/stream/write.rb +9 -3
  29. data/lib/mongo/monitoring.rb +38 -0
  30. data/lib/mongo/monitoring/command_log_subscriber.rb +10 -2
  31. data/lib/mongo/monitoring/event/command_failed.rb +11 -0
  32. data/lib/mongo/monitoring/event/command_started.rb +37 -2
  33. data/lib/mongo/monitoring/event/command_succeeded.rb +11 -0
  34. data/lib/mongo/monitoring/event/server_closed.rb +1 -1
  35. data/lib/mongo/monitoring/event/server_description_changed.rb +27 -4
  36. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +9 -2
  37. data/lib/mongo/monitoring/event/server_heartbeat_started.rb +9 -2
  38. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +9 -2
  39. data/lib/mongo/monitoring/event/server_opening.rb +1 -1
  40. data/lib/mongo/monitoring/event/topology_changed.rb +1 -1
  41. data/lib/mongo/monitoring/event/topology_closed.rb +1 -1
  42. data/lib/mongo/monitoring/event/topology_opening.rb +1 -1
  43. data/lib/mongo/monitoring/publishable.rb +6 -3
  44. data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +9 -1
  45. data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +1 -1
  46. data/lib/mongo/protocol/message.rb +36 -8
  47. data/lib/mongo/protocol/msg.rb +14 -0
  48. data/lib/mongo/protocol/serializers.rb +5 -2
  49. data/lib/mongo/server.rb +10 -3
  50. data/lib/mongo/server/connection.rb +4 -4
  51. data/lib/mongo/server/connection_base.rb +3 -1
  52. data/lib/mongo/server/description.rb +5 -0
  53. data/lib/mongo/server/monitor.rb +76 -44
  54. data/lib/mongo/server/monitor/connection.rb +55 -7
  55. data/lib/mongo/server/pending_connection.rb +14 -4
  56. data/lib/mongo/server/push_monitor.rb +173 -0
  57. data/{spec/runners/transactions/context.rb → lib/mongo/server/push_monitor/connection.rb} +9 -14
  58. data/lib/mongo/server_selector.rb +0 -1
  59. data/lib/mongo/server_selector/base.rb +579 -1
  60. data/lib/mongo/server_selector/nearest.rb +1 -6
  61. data/lib/mongo/server_selector/primary.rb +1 -6
  62. data/lib/mongo/server_selector/primary_preferred.rb +7 -10
  63. data/lib/mongo/server_selector/secondary.rb +1 -6
  64. data/lib/mongo/server_selector/secondary_preferred.rb +1 -7
  65. data/lib/mongo/session.rb +2 -0
  66. data/lib/mongo/socket.rb +20 -8
  67. data/lib/mongo/socket/ssl.rb +1 -1
  68. data/lib/mongo/socket/tcp.rb +1 -1
  69. data/lib/mongo/topology_version.rb +9 -0
  70. data/lib/mongo/utils.rb +62 -0
  71. data/lib/mongo/version.rb +1 -1
  72. data/spec/README.aws-auth.md +2 -2
  73. data/spec/integration/awaited_ismaster_spec.rb +28 -0
  74. data/spec/integration/change_stream_examples_spec.rb +6 -2
  75. data/spec/integration/check_clean_slate_spec.rb +16 -0
  76. data/spec/integration/client_construction_spec.rb +1 -0
  77. data/spec/integration/connect_single_rs_name_spec.rb +5 -2
  78. data/spec/integration/connection_spec.rb +7 -4
  79. data/spec/integration/crud_spec.rb +4 -4
  80. data/spec/integration/docs_examples_spec.rb +6 -0
  81. data/spec/integration/grid_fs_bucket_spec.rb +48 -0
  82. data/spec/integration/heartbeat_events_spec.rb +4 -23
  83. data/spec/integration/read_concern_spec.rb +1 -1
  84. data/spec/integration/retryable_errors_spec.rb +1 -1
  85. data/spec/integration/retryable_writes/shared/performs_legacy_retries.rb +2 -2
  86. data/spec/integration/retryable_writes/shared/performs_modern_retries.rb +3 -3
  87. data/spec/integration/retryable_writes/shared/performs_no_retries.rb +2 -2
  88. data/spec/integration/sdam_error_handling_spec.rb +37 -15
  89. data/spec/integration/sdam_events_spec.rb +77 -6
  90. data/spec/integration/sdam_prose_spec.rb +64 -0
  91. data/spec/integration/server_monitor_spec.rb +25 -1
  92. data/spec/integration/size_limit_spec.rb +7 -3
  93. data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +98 -0
  94. data/spec/integration/ssl_uri_options_spec.rb +2 -2
  95. data/spec/integration/zlib_compression_spec.rb +25 -0
  96. data/spec/lite_spec_helper.rb +12 -5
  97. data/spec/mongo/auth/aws/request_spec.rb +76 -0
  98. data/spec/mongo/auth/scram_spec.rb +1 -1
  99. data/spec/mongo/client_construction_spec.rb +207 -0
  100. data/spec/mongo/client_spec.rb +38 -3
  101. data/spec/mongo/cluster/topology/replica_set_spec.rb +52 -9
  102. data/spec/mongo/cluster/topology/single_spec.rb +4 -2
  103. data/spec/mongo/cluster_spec.rb +34 -35
  104. data/spec/mongo/collection/view/change_stream_resume_spec.rb +6 -6
  105. data/spec/mongo/collection_spec.rb +500 -0
  106. data/spec/mongo/database_spec.rb +245 -8
  107. data/spec/mongo/distinguishing_semaphore_spec.rb +63 -0
  108. data/spec/mongo/error/operation_failure_spec.rb +40 -0
  109. data/spec/mongo/index/view_spec.rb +2 -2
  110. data/spec/mongo/monitoring/event/server_description_changed_spec.rb +1 -4
  111. data/spec/mongo/protocol/msg_spec.rb +10 -0
  112. data/spec/mongo/semaphore_spec.rb +51 -0
  113. data/spec/mongo/server/connection_auth_spec.rb +2 -2
  114. data/spec/mongo/server_selector/nearest_spec.rb +23 -23
  115. data/spec/mongo/server_selector/primary_preferred_spec.rb +26 -26
  116. data/spec/mongo/server_selector/primary_spec.rb +9 -9
  117. data/spec/mongo/server_selector/secondary_preferred_spec.rb +22 -22
  118. data/spec/mongo/server_selector/secondary_spec.rb +18 -18
  119. data/spec/mongo/server_selector_spec.rb +4 -4
  120. data/spec/mongo/session_spec.rb +35 -0
  121. data/spec/runners/change_streams/test.rb +2 -2
  122. data/spec/runners/cmap.rb +1 -1
  123. data/spec/runners/command_monitoring.rb +3 -34
  124. data/spec/runners/crud/context.rb +9 -5
  125. data/spec/runners/crud/operation.rb +59 -27
  126. data/spec/runners/crud/spec.rb +0 -8
  127. data/spec/runners/crud/test.rb +1 -1
  128. data/spec/runners/sdam.rb +2 -2
  129. data/spec/runners/server_selection.rb +242 -28
  130. data/spec/runners/transactions.rb +12 -12
  131. data/spec/runners/transactions/operation.rb +151 -25
  132. data/spec/runners/transactions/test.rb +60 -16
  133. data/spec/spec_tests/command_monitoring_spec.rb +22 -12
  134. data/spec/spec_tests/crud_spec.rb +1 -1
  135. data/spec/spec_tests/data/change_streams/change-streams-errors.yml +4 -8
  136. data/spec/spec_tests/data/change_streams/change-streams-resume-whitelist.yml +66 -0
  137. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/MaxStalenessTooSmall.yml +15 -0
  138. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml +4 -3
  139. data/spec/spec_tests/data/max_staleness/Unknown/SmallMaxStaleness.yml +1 -0
  140. data/spec/spec_tests/data/sdam_integration/cancel-server-check.yml +96 -0
  141. data/spec/spec_tests/data/sdam_integration/connectTimeoutMS.yml +88 -0
  142. data/spec/spec_tests/data/sdam_integration/find-network-error.yml +83 -0
  143. data/spec/spec_tests/data/sdam_integration/find-shutdown-error.yml +116 -0
  144. data/spec/spec_tests/data/sdam_integration/insert-network-error.yml +86 -0
  145. data/spec/spec_tests/data/sdam_integration/insert-shutdown-error.yml +115 -0
  146. data/spec/spec_tests/data/sdam_integration/isMaster-command-error.yml +168 -0
  147. data/spec/spec_tests/data/sdam_integration/isMaster-network-error.yml +162 -0
  148. data/spec/spec_tests/data/sdam_integration/isMaster-timeout.yml +229 -0
  149. data/spec/spec_tests/data/sdam_integration/rediscover-quickly-after-step-down.yml +87 -0
  150. data/spec/spec_tests/max_staleness_spec.rb +4 -142
  151. data/spec/spec_tests/retryable_reads_spec.rb +2 -2
  152. data/spec/spec_tests/sdam_integration_spec.rb +13 -0
  153. data/spec/spec_tests/sdam_monitoring_spec.rb +1 -2
  154. data/spec/spec_tests/server_selection_spec.rb +4 -116
  155. data/spec/stress/cleanup_spec.rb +17 -2
  156. data/spec/stress/connection_pool_stress_spec.rb +10 -8
  157. data/spec/support/child_process_helper.rb +78 -0
  158. data/spec/support/client_registry.rb +1 -0
  159. data/spec/support/cluster_config.rb +4 -0
  160. data/spec/support/event_subscriber.rb +123 -33
  161. data/spec/support/keyword_struct.rb +26 -0
  162. data/spec/support/shared/server_selector.rb +13 -1
  163. data/spec/support/spec_config.rb +38 -13
  164. data/spec/support/spec_organizer.rb +129 -0
  165. data/spec/support/spec_setup.rb +1 -1
  166. data/spec/support/utils.rb +46 -0
  167. metadata +992 -942
  168. metadata.gz.sig +0 -0
  169. data/lib/mongo/server_selector/selectable.rb +0 -560
  170. data/spec/runners/sdam_monitoring.rb +0 -89
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f5e58cfb13fc60fb774becdceb53b4c79cd1461a2cbd3f2dcfd44216f083da8
4
- data.tar.gz: df770f62315fa3ef4db7a32d796c093759a38c1e6c4e6eaa183789b1700f5ed9
3
+ metadata.gz: db8c88ed58f349728a9361db891376926e93450f4a8ac86aae0bc67599c91a7b
4
+ data.tar.gz: b4f7daaf1d43b55839c1f689504d4f1c212c638e45f6d2211732bc07a41f77e5
5
5
  SHA512:
6
- metadata.gz: a51aa55384766e936b0bbc4ad2f99c9fb22ad03d10e04a4f65a5492c16764c92495ae17c5e1ba521c96e3fe25c5b2d787ab985939947eeffd0957a54ceef4078
7
- data.tar.gz: 6d8ca3220081914ec39479c4c9ebb4a6f070be7f4840fa98f0b6cf28f9c3a9c8082264e9b3b7b0d588cda54f97c9f687e36fd467576a396527d80b493c8a52e7
6
+ metadata.gz: 22471d116179fff42716707a40fa18ebc3c53492a6313bb16be96f447db725110b8ed0b3ee8e199026098a59b8f2941290cd1493249c8e9120ec2849e0229fa1
7
+ data.tar.gz: 8965e04c6926343e4363110d010e14b1a8fde146eb4e239f620005f251ff24b63393bc13f5f332ac4d8aa39cc790a66f080401d9b0923c54a8b40b7d80aa25ea
Binary file
data.tar.gz.sig CHANGED
@@ -1,5 +1 @@
1
- �⋎�L��`�*Vd��
2
- �J���5_E:���
3
- �&E�!��Z�E:���%-%sZY��_��5 �o�����I�AB�׵X��������������R��7�88_}lB�<&06�.W�����eJu�A�
4
- M��QhuF-0�)��_ b���y��i�9q
5
- Q��E:8�H��7 0'� �����º���q�<����[�_���[��k_s���� >��RՐ;�5iq.�[��~��'9�������%
1
+ �7�$��7�NEQg��KLJ
data/Rakefile CHANGED
@@ -45,16 +45,16 @@ namespace :spec do
45
45
  client = ClientRegistry.instance.global_client('authorized')
46
46
  client.database.command(ping: 1)
47
47
  deadline = Time.now + 300
48
- while Time.now < deadline
49
- if client.cluster.send(:sessions_supported?)
48
+ loop do
49
+ begin
50
+ client.cluster.validate_session_support!
50
51
  break
52
+ rescue Mongo::Error::SessionsNotSupported
53
+ if Time.now >= deadline
54
+ raise "Sessions did not become supported in 300 seconds"
55
+ end
56
+ client.cluster.scan!
51
57
  end
52
- sleep 1
53
- client.close
54
- client.reconnect
55
- end
56
- unless client.cluster.send(:sessions_supported?)
57
- raise "Sessions did not become supported in the allowed time"
58
58
  end
59
59
  end
60
60
 
@@ -71,7 +71,13 @@ namespace :spec do
71
71
  SpecConfig.instance.print_summary
72
72
  end
73
73
 
74
- task :ci => ['spec:prepare', :spec]
74
+ task :ci => ['spec:prepare'] do
75
+ $: << File.join(File.dirname(__FILE__), 'spec')
76
+
77
+ require 'support/spec_organizer'
78
+
79
+ SpecOrganizer.new.run
80
+ end
75
81
  end
76
82
 
77
83
  namespace :release do
@@ -34,6 +34,7 @@ require 'bson'
34
34
  require 'mongo/id'
35
35
  require 'mongo/bson'
36
36
  require 'mongo/semaphore'
37
+ require 'mongo/distinguishing_semaphore'
37
38
  require 'mongo/options'
38
39
  require 'mongo/loggable'
39
40
  require 'mongo/cluster_time'
@@ -54,9 +55,11 @@ require 'mongo/collection'
54
55
  require 'mongo/database'
55
56
  require 'mongo/crypt'
56
57
  require 'mongo/client' # Purposely out-of-order so that database is loaded first
58
+ require 'mongo/client_encryption'
57
59
  require 'mongo/dbref'
58
60
  require 'mongo/grid'
59
61
  require 'mongo/index'
62
+ require 'mongo/lint'
60
63
  require 'mongo/server'
61
64
  require 'mongo/server_selector'
62
65
  require 'mongo/session'
@@ -66,5 +69,4 @@ require 'mongo/timeout'
66
69
  require 'mongo/uri'
67
70
  require 'mongo/version'
68
71
  require 'mongo/write_concern'
69
- require 'mongo/lint'
70
- require 'mongo/client_encryption'
72
+ require 'mongo/utils'
@@ -12,7 +12,9 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- autoload :Net, 'net/http'
15
+ module Net
16
+ autoload :HTTP, 'net/http'
17
+ end
16
18
 
17
19
  module Mongo
18
20
  module Auth
@@ -86,7 +88,7 @@ module Mongo
86
88
  # @return [ String ] formatted_time ISO8601-formatted time of the
87
89
  # request, as would be used in X-Amz-Date header.
88
90
  def formatted_time
89
- @formatted_time ||= @time.utc.strftime('%Y%m%dT%H%M%SZ')
91
+ @formatted_time ||= @time.getutc.strftime('%Y%m%dT%H%M%SZ')
90
92
  end
91
93
 
92
94
  # @return [ String ] formatted_date YYYYMMDD formatted date of the request.
@@ -170,6 +170,7 @@ module Mongo
170
170
  :ordered => ordered?,
171
171
  :operation_id => operation_id,
172
172
  :bypass_document_validation => !!options[:bypass_document_validation],
173
+ :max_time_ms => options[:max_time_ms],
173
174
  :options => options,
174
175
  :id_generator => client.options[:id_generator],
175
176
  :session => session
@@ -55,6 +55,7 @@ module Mongo
55
55
  :auth_mech_properties,
56
56
  :auth_source,
57
57
  :auto_encryption_options,
58
+ :bg_error_backtrace,
58
59
  :cleanup,
59
60
  :compressors,
60
61
  :direct_connection,
@@ -213,6 +214,10 @@ module Mongo
213
214
  # use. One of :mongodb_cr, :mongodb_x509, :plain, :scram, :scram256
214
215
  # @option options [ Hash ] :auth_mech_properties
215
216
  # @option options [ String ] :auth_source The source to authenticate from.
217
+ # @option options [ true | false | nil | Integer ] :bg_error_backtrace
218
+ # Experimental. Set to true to log complete backtraces for errors in
219
+ # background threads. Set to false or nil to not log backtraces. Provide
220
+ # a positive integer to log up to that many backtrace lines.
216
221
  # @option options [ Array<String> ] :compressors A list of potential
217
222
  # compressors to use, in order of preference. The driver chooses the
218
223
  # first compressor that is also supported by the server. Currently the
@@ -443,6 +448,12 @@ module Mongo
443
448
  @srv_records = uri.srv_records
444
449
  else
445
450
  addresses = addresses_or_uri
451
+ addresses.each do |addr|
452
+ if addr =~ /\Amongodb(\+srv)?:\/\//i
453
+ raise ArgumentError, "Host '#{addr}' should not contain protocol. Did you mean to not use an array?"
454
+ end
455
+ end
456
+
446
457
  @srv_records = nil
447
458
  end
448
459
 
@@ -489,7 +500,6 @@ module Mongo
489
500
  end
490
501
  end
491
502
 
492
- yield(self) if block_given?
493
503
  rescue
494
504
  begin
495
505
  @cluster.disconnect!
@@ -499,6 +509,14 @@ module Mongo
499
509
  end
500
510
  raise
501
511
  end
512
+
513
+ if block_given?
514
+ begin
515
+ yield(self)
516
+ ensure
517
+ close
518
+ end
519
+ end
502
520
  end
503
521
 
504
522
  # @api private
@@ -802,6 +820,13 @@ module Mongo
802
820
  # @param [ Hash ] filter The filter criteria for getting a list of databases.
803
821
  # @param [ Hash ] opts The command options.
804
822
  #
823
+ # @option opts [ true, false ] :authorized_databases A flag that determines
824
+ # which databases are returned based on user privileges when access control
825
+ # is enabled
826
+ #
827
+ # See https://docs.mongodb.com/manual/reference/command/listDatabases/
828
+ # for more information and usage.
829
+ #
805
830
  # @return [ Array<String> ] The names of the databases.
806
831
  #
807
832
  # @since 2.0.5
@@ -818,6 +843,13 @@ module Mongo
818
843
  # @param [ true, false ] name_only Whether to only return each database name without full metadata.
819
844
  # @param [ Hash ] opts The command options.
820
845
  #
846
+ # @option opts [ true, false ] :authorized_databases A flag that determines
847
+ # which databases are returned based on user privileges when access control
848
+ # is enabled
849
+ #
850
+ # See https://docs.mongodb.com/manual/reference/command/listDatabases/
851
+ # for more information and usage.
852
+ #
821
853
  # @return [ Array<Hash> ] The info for each database.
822
854
  #
823
855
  # @since 2.0.5
@@ -825,6 +857,7 @@ module Mongo
825
857
  cmd = { listDatabases: 1 }
826
858
  cmd[:nameOnly] = !!name_only
827
859
  cmd[:filter] = filter unless filter.empty?
860
+ cmd[:authorizedDatabases] = true if opts[:authorized_databases]
828
861
  use(Database::ADMIN).database.read_command(cmd, opts).first[Database::DATABASES]
829
862
  end
830
863
 
@@ -865,8 +898,16 @@ module Mongo
865
898
  #
866
899
  # @since 2.5.0
867
900
  def start_session(options = {})
868
- get_session(options.merge(implicit: false)) or
869
- raise Error::InvalidSession.new(Session::SESSIONS_NOT_SUPPORTED)
901
+ session = get_session!(options.merge(implicit: false))
902
+ if block_given?
903
+ begin
904
+ yield session
905
+ ensure
906
+ session.end_session
907
+ end
908
+ else
909
+ session
910
+ end
870
911
  end
871
912
 
872
913
  # As of version 3.6 of the MongoDB server, a ``$changeStream`` pipeline stage is supported
@@ -912,6 +953,59 @@ module Mongo
912
953
  options)
913
954
  end
914
955
 
956
+ # Returns a session to use for operations if possible.
957
+ #
958
+ # If :session option is set, validates that session and returns it.
959
+ # Otherwise, if deployment supports sessions, creates a new session and
960
+ # returns it. When a new session is created, the session will be implicit
961
+ # (lifecycle is managed by the driver) if the :implicit option is given,
962
+ # otherwise the session will be explicit (lifecycle managed by the
963
+ # application). If deployment does not support session, returns nil.
964
+ #
965
+ # @option options [ true | false ] :implicit When no session is passed in,
966
+ # whether to create an implicit session.
967
+ # @option options [ Session ] :session The session to validate and return.
968
+ #
969
+ # @return [ Session | nil ] Session object or nil if sessions are not
970
+ # supported by the deployment.
971
+ #
972
+ # @api private
973
+ def get_session(options = {})
974
+ get_session!(options)
975
+ rescue Error::SessionsNotSupported
976
+ nil
977
+ end
978
+
979
+ # Creates a session to use for operations if possible and yields it to
980
+ # the provided block.
981
+ #
982
+ # If :session option is set, validates that session and uses it.
983
+ # Otherwise, if deployment supports sessions, creates a new session and
984
+ # uses it. When a new session is created, the session will be implicit
985
+ # (lifecycle is managed by the driver) if the :implicit option is given,
986
+ # otherwise the session will be explicit (lifecycle managed by the
987
+ # application). If deployment does not support session, yields nil to
988
+ # the block.
989
+ #
990
+ # When the block finishes, if the session was created and was implicit,
991
+ # or if an implicit session was passed in, the session is ended which
992
+ # returns it to the pool of available sessions.
993
+ #
994
+ # @option options [ true | false ] :implicit When no session is passed in,
995
+ # whether to create an implicit session.
996
+ # @option options [ Session ] :session The session to validate and return.
997
+ #
998
+ # @api private
999
+ def with_session(options = {}, &block)
1000
+ session = get_session(options)
1001
+
1002
+ yield session
1003
+ ensure
1004
+ if session && session.implicit?
1005
+ session.end_session
1006
+ end
1007
+ end
1008
+
915
1009
  private
916
1010
 
917
1011
  # Create a new encrypter object using the client's auto encryption options
@@ -944,30 +1038,34 @@ module Mongo
944
1038
  close_encrypter
945
1039
  end
946
1040
 
947
- # If options[:session] is set, validates that session and returns it.
948
- # If deployment supports sessions, creates a new session and returns it.
949
- # The session is implicit unless options[:implicit] is given.
950
- # If deployment does not support session, returns nil.
1041
+ # Returns a session to use for operations.
951
1042
  #
952
- # @return [ Session | nil ] Session object or nil if sessions are not
953
- # supported by the deployment.
954
- def get_session(options = {})
1043
+ # If :session option is set, validates that session and returns it.
1044
+ # Otherwise, if deployment supports sessions, creates a new session and
1045
+ # returns it. When a new session is created, the session will be implicit
1046
+ # (lifecycle is managed by the driver) if the :implicit option is given,
1047
+ # otherwise the session will be explicit (lifecycle managed by the
1048
+ # application). If deployment does not support session, raises
1049
+ # Error::InvalidSession.
1050
+ #
1051
+ # @option options [ true | false ] :implicit When no session is passed in,
1052
+ # whether to create an implicit session.
1053
+ # @option options [ Session ] :session The session to validate and return.
1054
+ #
1055
+ # @return [ Session ] A session object.
1056
+ #
1057
+ # @raise Error::SessionsNotSupported if sessions are not supported by
1058
+ # the deployment.
1059
+ #
1060
+ # @api private
1061
+ def get_session!(options = {})
955
1062
  if options[:session]
956
1063
  return options[:session].validate!(self)
957
1064
  end
958
1065
 
959
- if cluster.sessions_supported?
960
- Session.new(cluster.session_pool.checkout, self, { implicit: true }.merge(options))
961
- end
962
- end
1066
+ cluster.validate_session_support!
963
1067
 
964
- def with_session(options = {}, &block)
965
- session = get_session(options)
966
- yield(session)
967
- ensure
968
- if session && session.implicit?
969
- session.end_session
970
- end
1068
+ Session.new(cluster.session_pool.checkout, self, { implicit: true }.merge(options))
971
1069
  end
972
1070
 
973
1071
  def initialize_copy(original)
@@ -1038,6 +1136,30 @@ module Mongo
1038
1136
  if options[:direct_connection] == false && options[:connect] && options[:connect].to_sym == :direct
1039
1137
  raise ArgumentError, "Conflicting client options: direct_connection=false and connect=#{options[:connect]}"
1040
1138
  end
1139
+
1140
+ %i(connect_timeout socket_timeout).each do |key|
1141
+ if value = options[key]
1142
+ unless Numeric === value
1143
+ raise ArgumentError, "#{key} must be a non-negative number: #{value}"
1144
+ end
1145
+ if value < 0
1146
+ raise ArgumentError, "#{key} must be a non-negative number: #{value}"
1147
+ end
1148
+ end
1149
+ end
1150
+
1151
+ if value = options[:bg_error_backtrace]
1152
+ case value
1153
+ when Integer
1154
+ if value <= 0
1155
+ raise ArgumentError, ":bg_error_backtrace option value must be true, false, nil or a positive integer: #{value}"
1156
+ end
1157
+ when true
1158
+ # OK
1159
+ else
1160
+ raise ArgumentError, ":bg_error_backtrace option value must be true, false, nil or a positive integer: #{value}"
1161
+ end
1162
+ end
1041
1163
  end
1042
1164
 
1043
1165
  # Validates all authentication-related options after they are set on the client
@@ -395,7 +395,7 @@ module Mongo
395
395
  #
396
396
  # @since 2.0.0
397
397
  def servers
398
- topology.servers(servers_list.compact).compact
398
+ topology.servers(servers_list)
399
399
  end
400
400
 
401
401
  # The addresses in the cluster.
@@ -599,11 +599,14 @@ module Mongo
599
599
  # respective server is cleared. Set this option to true to keep the
600
600
  # existing connection pool (required when handling not master errors
601
601
  # on 4.2+ servers).
602
+ # @option aptions [ true | false ] :awaited Whether the updated description
603
+ # was a result of processing an awaited ismaster.
602
604
  #
603
605
  # @api private
604
606
  def run_sdam_flow(previous_desc, updated_desc, options = {})
605
607
  @sdam_flow_lock.synchronize do
606
- flow = SdamFlow.new(self, previous_desc, updated_desc)
608
+ flow = SdamFlow.new(self, previous_desc, updated_desc,
609
+ awaited: options[:awaited])
607
610
  flow.server_description_changed
608
611
 
609
612
  # SDAM flow may alter the updated description - grab the final
@@ -886,21 +889,26 @@ module Mongo
886
889
  end
887
890
  end
888
891
 
889
- # Returns whether the deployment that the driver is connected to supports
890
- # sessions.
892
+ # Raises Error::SessionsNotAvailable if the deployment that the driver
893
+ # is connected to does not support sessions.
891
894
  #
892
895
  # Session support may change over time, for example due to servers in the
893
- # deployment being upgraded or downgraded. This method returns the
894
- # current information if the client is connected to at least one data
895
- # bearing server. If the client is currently not connected to any data
896
- # bearing servers, this method returns the last known value for whether
897
- # the deployment supports sessions.
896
+ # deployment being upgraded or downgraded. If the client is currently not
897
+ # connected to any data bearing servers, this method considers the state
898
+ # of session support as of when the client was last connected to at
899
+ # least one server. If the client has never connected to any servers,
900
+ # the deployment is considered to not support sessions.
898
901
  #
899
- # @return [ true | false ] Whether deployment supports sessions.
900
902
  # @api private
901
- def sessions_supported?
902
- if topology.data_bearing_servers?
903
- return !!topology.logical_session_timeout
903
+ def validate_session_support!
904
+ @state_change_lock.synchronize do
905
+ @sdam_flow_lock.synchronize do
906
+ if topology.data_bearing_servers?
907
+ unless topology.logical_session_timeout
908
+ raise_sessions_not_supported
909
+ end
910
+ end
911
+ end
904
912
  end
905
913
 
906
914
  # No data bearing servers known - perform server selection to try to
@@ -908,12 +916,24 @@ module Mongo
908
916
  # assessment of whether sessions are currently supported.
909
917
  begin
910
918
  ServerSelector.get(mode: :primary_preferred).select_server(self)
911
- !!topology.logical_session_timeout
919
+ @state_change_lock.synchronize do
920
+ @sdam_flow_lock.synchronize do
921
+ unless topology.logical_session_timeout
922
+ raise_sessions_not_supported
923
+ end
924
+ end
925
+ end
912
926
  rescue Error::NoServerAvailable
913
927
  # We haven't been able to contact any servers - use last known
914
- # value for esssion support.
915
- @update_lock.synchronize do
916
- @sessions_supported || false
928
+ # value for session support.
929
+ @state_change_lock.synchronize do
930
+ @sdam_flow_lock.synchronize do
931
+ @update_lock.synchronize do
932
+ unless @sessions_supported
933
+ raise_sessions_not_supported
934
+ end
935
+ end
936
+ end
917
937
  end
918
938
  end
919
939
  end
@@ -952,6 +972,22 @@ module Mongo
952
972
  end
953
973
  end
954
974
  end
975
+
976
+ def raise_sessions_not_supported
977
+ # Intentionally using @servers instead of +servers+ here because we
978
+ # are supposed to be already holding the @update_lock and we cannot
979
+ # recursively acquire it again.
980
+ offending_servers = @servers.select do |server|
981
+ server.description.data_bearing? && server.logical_session_timeout.nil?
982
+ end
983
+ reason = if offending_servers.empty?
984
+ "There are no known data bearing servers (current seeds: #{@servers.map(&:address).map(&:seed).join(', ')})"
985
+ else
986
+ "The following servers have null logical session timeout: #{offending_servers.map(&:address).map(&:seed).join(', ')}"
987
+ end
988
+ msg = "The deployment that the driver is connected to does not support sessions: #{reason}"
989
+ raise Error::SessionsNotSupported, msg
990
+ end
955
991
  end
956
992
  end
957
993