mongo 2.14.0 → 2.14.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/mongo/background_thread.rb +11 -0
  4. data/lib/mongo/cluster/sdam_flow.rb +14 -0
  5. data/lib/mongo/cluster.rb +0 -26
  6. data/lib/mongo/collection/view/iterable.rb +16 -6
  7. data/lib/mongo/collection.rb +2 -0
  8. data/lib/mongo/cursor.rb +10 -0
  9. data/lib/mongo/database.rb +14 -2
  10. data/lib/mongo/grid/fs_bucket.rb +37 -37
  11. data/lib/mongo/operation/parallel_scan/command.rb +1 -2
  12. data/lib/mongo/operation/shared/read_preference_supported.rb +38 -36
  13. data/lib/mongo/operation/shared/sessions_supported.rb +3 -2
  14. data/lib/mongo/protocol/msg.rb +2 -2
  15. data/lib/mongo/protocol/query.rb +11 -11
  16. data/lib/mongo/server/connection_pool.rb +0 -2
  17. data/lib/mongo/server.rb +0 -14
  18. data/lib/mongo/server_selector/secondary_preferred.rb +2 -7
  19. data/lib/mongo/srv/monitor.rb +0 -11
  20. data/lib/mongo/version.rb +1 -1
  21. data/spec/integration/query_cache_spec.rb +45 -0
  22. data/spec/integration/sdam_error_handling_spec.rb +1 -1
  23. data/spec/integration/sdam_events_spec.rb +3 -5
  24. data/spec/integration/secondary_reads_spec.rb +102 -0
  25. data/spec/mongo/cluster_spec.rb +2 -18
  26. data/spec/mongo/index/view_spec.rb +4 -2
  27. data/spec/mongo/operation/read_preference_legacy_spec.rb +9 -19
  28. data/spec/mongo/operation/read_preference_op_msg_spec.rb +3 -3
  29. data/spec/mongo/server/app_metadata_shared.rb +33 -7
  30. data/spec/mongo/server_selector/secondary_preferred_spec.rb +6 -6
  31. data/spec/runners/transactions/operation.rb +13 -2
  32. data/spec/shared/bin/get-mongodb-download-url +17 -0
  33. data/spec/shared/lib/mrss/cluster_config.rb +221 -0
  34. data/spec/shared/lib/mrss/constraints.rb +43 -0
  35. data/spec/shared/lib/mrss/docker_runner.rb +265 -0
  36. data/spec/shared/lib/mrss/lite_constraints.rb +16 -0
  37. data/spec/shared/lib/mrss/server_version_registry.rb +115 -0
  38. data/spec/shared/lib/mrss/spec_organizer.rb +3 -0
  39. data/spec/shared/lib/mrss/utils.rb +15 -0
  40. data/spec/shared/share/Dockerfile.erb +231 -0
  41. data/spec/shared/shlib/distro.sh +73 -0
  42. data/spec/shared/shlib/server.sh +290 -0
  43. data/spec/shared/shlib/set_env.sh +128 -0
  44. data/spec/solo/clean_exit_spec.rb +21 -0
  45. data/spec/support/client_registry.rb +8 -4
  46. data/spec/support/client_registry_macros.rb +4 -4
  47. data/spec/support/spec_config.rb +12 -0
  48. data/spec/support/spec_setup.rb +48 -38
  49. data.tar.gz.sig +0 -0
  50. metadata +1005 -983
  51. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7bfb22d052a842e256744a88c7e3cbd3b958643cc3e0f4425bf97be8f913f167
4
- data.tar.gz: b63f3605ad8223e99a5f6a62aabff94a8c8600095b84265b6b4deb38e7223013
3
+ metadata.gz: 02e085995ec501175b551369fff92fd59b44787da27cb9f84b93dafef4d0762b
4
+ data.tar.gz: f7ac041c7d89ad98207a6038ab44263aed36ae4fbdc925107a0f74772889ff48
5
5
  SHA512:
6
- metadata.gz: 85566c0080b9bcfa67438a365114dd062425df557040e05c0db0448aedd37a0f52dae5b8612b08af196a9e35c1e5b41553a89607d780d187d4e2e28602471c76
7
- data.tar.gz: 5172ab2d71a8d7e048fcb2d87e7219024a2028c3909f2cce48081d1b3014071df13b02426019508bda6542e4870992ca869cd3393caf3aaf6483ad4c74f1abf3
6
+ metadata.gz: 61512bc1653f1b2d9e5d73db7acbfd0725931cf7e234aad7b709086e76037f287da12e6c893c6ae84429113fd83cf195a296e0f3f01fa3974670106d1b19e8d4
7
+ data.tar.gz: 198f03ef56d8f280962ca212e411bf97a04cd518d7e1f82628f3bf4c274cf897c872e1d109af1ee94627803de2d3d8b37afbd239f87fe1c6a74e6813a2f65201
checksums.yaml.gz.sig CHANGED
Binary file
@@ -20,6 +20,17 @@ module Mongo
20
20
  # compatibility reasons. However using these methods outside of the driver
21
21
  # is deprecated.
22
22
  #
23
+ # @note Do not start or stop background threads in finalizers. See
24
+ # https://jira.mongodb.org/browse/RUBY-2453 and
25
+ # https://bugs.ruby-lang.org/issues/16288. When interpreter exits,
26
+ # background threads are stopped first and finalizers are invoked next,
27
+ # and MRI's internal data structures are basically corrupt at this point
28
+ # if threads are being referenced. Prior to interpreter shutdown this
29
+ # means threads cannot be stopped by objects going out of scope, but
30
+ # most likely the threads hold references to said objects anyway if
31
+ # work is being performed thus the objects wouldn't go out of scope in
32
+ # the first place.
33
+ #
23
34
  # @api private
24
35
  module BackgroundThread
25
36
  include Loggable
@@ -178,6 +178,7 @@ class Mongo::Cluster
178
178
  raise ArgumentError, "Unknown topology #{topology.class}"
179
179
  end
180
180
 
181
+ verify_invariants
181
182
  commit_changes
182
183
  disconnect_servers
183
184
  end
@@ -363,6 +364,9 @@ class Mongo::Cluster
363
364
  end
364
365
  end
365
366
  end
367
+
368
+ verify_invariants
369
+
366
370
  added_servers
367
371
  end
368
372
 
@@ -599,5 +603,15 @@ class Mongo::Cluster
599
603
 
600
604
  @previous_server_descriptions != server_descriptions
601
605
  end
606
+
607
+ def verify_invariants
608
+ if Mongo::Lint.enabled?
609
+ if cluster.topology.single?
610
+ if cluster.servers_list.length > 1
611
+ raise Mongo::Error::LintError, "Trying to create a single topology with multiple servers: #{cluster.servers_list}"
612
+ end
613
+ end
614
+ end
615
+ end
602
616
  end
603
617
  end
data/lib/mongo/cluster.rb CHANGED
@@ -199,9 +199,6 @@ module Mongo
199
199
  @cursor_reaper, @socket_reaper,
200
200
  ], options)
201
201
 
202
- ObjectSpace.define_finalizer(self, self.class.finalize(
203
- {}, @periodic_executor, @session_pool))
204
-
205
202
  @periodic_executor.run!
206
203
  end
207
204
 
@@ -445,25 +442,6 @@ module Mongo
445
442
  # @api private
446
443
  attr_reader :server_selection_semaphore
447
444
 
448
- # Finalize the cluster for garbage collection.
449
- #
450
- # @example Finalize the cluster.
451
- # Cluster.finalize(pools)
452
- #
453
- # @param [ Hash<Address, Server::ConnectionPool> ] pools Ignored.
454
- # @param [ PeriodicExecutor ] periodic_executor The periodic executor.
455
- # @param [ SessionPool ] session_pool The session pool.
456
- #
457
- # @return [ Proc ] The Finalizer.
458
- #
459
- # @since 2.2.0
460
- def self.finalize(pools, periodic_executor, session_pool)
461
- proc do
462
- session_pool.end_sessions
463
- periodic_executor.stop!
464
- end
465
- end
466
-
467
445
  # Closes the cluster.
468
446
  #
469
447
  # @note Applications should call Client#close to disconnect from
@@ -953,10 +931,6 @@ module Mongo
953
931
  monitor_options = Utils.shallow_symbolize_keys(options.merge(
954
932
  timeout: options[:connect_timeout] || Server::CONNECT_TIMEOUT))
955
933
  @srv_monitor = _srv_monitor = Srv::Monitor.new(self, **monitor_options)
956
- finalizer = lambda do
957
- _srv_monitor.stop!
958
- end
959
- ObjectSpace.define_finalizer(self, finalizer)
960
934
  end
961
935
  @srv_monitor.run!
962
936
  end
@@ -35,18 +35,28 @@ module Mongo
35
35
  #
36
36
  # @yieldparam [ Hash ] Each matching document.
37
37
  def each
38
- @cursor = if use_query_cache? && cached_cursor
38
+ # If the caching cursor is closed and was not fully iterated,
39
+ # the documents we have in it are not the complete result set and
40
+ # we have no way of completing that iteration.
41
+ # Therefore, discard that cursor and start iteration again.
42
+ # The case of the caching cursor not being closed and not having
43
+ # been fully iterated isn't tested - see RUBY-2773.
44
+ @cursor = if use_query_cache? && cached_cursor && (
45
+ cached_cursor.fully_iterated? || !cached_cursor.closed?
46
+ )
39
47
  cached_cursor
40
48
  else
41
49
  session = client.send(:get_session, @options)
42
- select_cursor(session)
50
+ select_cursor(session).tap do |cursor|
51
+ if use_query_cache?
52
+ # No need to store the cursor in the query cache if there is
53
+ # already a cached cursor stored at this key.
54
+ QueryCache.set(cursor, **cache_options)
55
+ end
56
+ end
43
57
  end
44
58
 
45
59
  if use_query_cache?
46
- # No need to store the cursor in the query cache if there is
47
- # already a cached cursor stored at this key.
48
- QueryCache.set(@cursor, **cache_options) unless cached_cursor
49
-
50
60
  # If a query with a limit is performed, the query cache will
51
61
  # re-use results from an earlier query with the same or larger
52
62
  # limit, and then impose the lower limit during iteration.
@@ -593,6 +593,8 @@ module Mongo
593
593
  # @param [ Array<Hash> ] documents The documents to insert.
594
594
  # @param [ Hash ] options The insert options.
595
595
  #
596
+ # @option options [ true | false ] :ordered Whether the operations
597
+ # should be executed in order.
596
598
  # @option options [ Session ] :session The session to use for the operation.
597
599
  #
598
600
  # @return [ Result ] The database response wrapper.
data/lib/mongo/cursor.rb CHANGED
@@ -209,10 +209,12 @@ module Mongo
209
209
  unless closed?
210
210
  if exhausted?
211
211
  close
212
+ @fully_iterated = true
212
213
  raise StopIteration
213
214
  end
214
215
  @documents = get_more
215
216
  else
217
+ @fully_iterated = true
216
218
  raise StopIteration
217
219
  end
218
220
  else
@@ -229,6 +231,9 @@ module Mongo
229
231
  # over the last document, or if the batch is empty
230
232
  if @documents.size <= 1
231
233
  cache_batch_resume_token
234
+ if closed?
235
+ @fully_iterated = true
236
+ end
232
237
  end
233
238
 
234
239
  return @documents.shift
@@ -348,6 +353,11 @@ module Mongo
348
353
  process(get_more_operation.execute(@server, client: client))
349
354
  end
350
355
 
356
+ # @api private
357
+ def fully_iterated?
358
+ !!@fully_iterated
359
+ end
360
+
351
361
  private
352
362
 
353
363
  def exhausted?
@@ -321,8 +321,20 @@ module Mongo
321
321
 
322
322
  # Get the Grid "filesystem" for this database.
323
323
  #
324
- # @example Get the GridFS.
325
- # database.fs
324
+ # @param [ Hash ] options The GridFS options.
325
+ #
326
+ # @option options [ String ] :bucket_name The prefix for the files and chunks
327
+ # collections.
328
+ # @option options [ Integer ] :chunk_size Override the default chunk
329
+ # size.
330
+ # @option options [ String ] :fs_name The prefix for the files and chunks
331
+ # collections.
332
+ # @option options [ String ] :read The read preference.
333
+ # @option options [ Session ] :session The session to use.
334
+ # @option options [ Hash ] :write Deprecated. Equivalent to :write_concern
335
+ # option.
336
+ # @option options [ Hash ] :write_concern The write concern options.
337
+ # Can be :w => Integer|String, :fsync => Boolean, :j => Boolean.
326
338
  #
327
339
  # @return [ Grid::FSBucket ] The GridFS for the database.
328
340
  #
@@ -36,6 +36,43 @@ module Mongo
36
36
  # @since 2.1.0
37
37
  FILES_INDEX = { filename: 1, uploadDate: 1 }.freeze
38
38
 
39
+ # Create the GridFS.
40
+ #
41
+ # @example Create the GridFS.
42
+ # Grid::FSBucket.new(database)
43
+ #
44
+ # @param [ Database ] database The database the files reside in.
45
+ # @param [ Hash ] options The GridFS options.
46
+ #
47
+ # @option options [ String ] :bucket_name The prefix for the files and chunks
48
+ # collections.
49
+ # @option options [ Integer ] :chunk_size Override the default chunk
50
+ # size.
51
+ # @option options [ String ] :fs_name The prefix for the files and chunks
52
+ # collections.
53
+ # @option options [ String ] :read The read preference.
54
+ # @option options [ Session ] :session The session to use.
55
+ # @option options [ Hash ] :write Deprecated. Equivalent to :write_concern
56
+ # option.
57
+ # @option options [ Hash ] :write_concern The write concern options.
58
+ # Can be :w => Integer|String, :fsync => Boolean, :j => Boolean.
59
+ #
60
+ # @since 2.0.0
61
+ def initialize(database, options = {})
62
+ @database = database
63
+ @options = options.dup
64
+ =begin WriteConcern object support
65
+ if @options[:write_concern].is_a?(WriteConcern::Base)
66
+ # Cache the instance so that we do not needlessly reconstruct it.
67
+ @write_concern = @options[:write_concern]
68
+ @options[:write_concern] = @write_concern.options
69
+ end
70
+ =end
71
+ @options.freeze
72
+ @chunks_collection = database[chunks_name]
73
+ @files_collection = database[files_name]
74
+ end
75
+
39
76
  # @return [ Collection ] chunks_collection The chunks collection.
40
77
  #
41
78
  # @since 2.0.0
@@ -133,43 +170,6 @@ module Mongo
133
170
  file.id
134
171
  end
135
172
 
136
- # Create the GridFS.
137
- #
138
- # @example Create the GridFS.
139
- # Grid::FSBucket.new(database)
140
- #
141
- # @param [ Database ] database The database the files reside in.
142
- # @param [ Hash ] options The GridFS options.
143
- #
144
- # @option options [ String ] :fs_name The prefix for the files and chunks
145
- # collections.
146
- # @option options [ String ] :bucket_name The prefix for the files and chunks
147
- # collections.
148
- # @option options [ Integer ] :chunk_size Override the default chunk
149
- # size.
150
- # @option options [ String ] :read The read preference.
151
- # @option options [ Session ] :session The session to use.
152
- # @option options [ Hash ] :write Deprecated. Equivalent to :write_concern
153
- # option.
154
- # @option options [ Hash ] :write_concern The write concern options.
155
- # Can be :w => Integer|String, :fsync => Boolean, :j => Boolean.
156
- #
157
- # @since 2.0.0
158
- def initialize(database, options = {})
159
- @database = database
160
- @options = options.dup
161
- =begin WriteConcern object support
162
- if @options[:write_concern].is_a?(WriteConcern::Base)
163
- # Cache the instance so that we do not needlessly reconstruct it.
164
- @write_concern = @options[:write_concern]
165
- @options[:write_concern] = @write_concern.options
166
- end
167
- =end
168
- @options.freeze
169
- @chunks_collection = database[chunks_name]
170
- @files_collection = database[files_name]
171
- end
172
-
173
173
  # Get the prefix for the GridFS
174
174
  #
175
175
  # @example Get the prefix.
@@ -37,8 +37,7 @@ module Mongo
37
37
  read_concern)
38
38
  end
39
39
  sel[:maxTimeMS] = max_time_ms if max_time_ms
40
- update_selector_for_read_pref(sel, connection)
41
- sel
40
+ add_read_preference_legacy(sel, connection)
42
41
  end
43
42
 
44
43
  def message(connection)
@@ -36,47 +36,44 @@ module Mongo
36
36
  #
37
37
  # @since 2.0.0
38
38
  def options(connection)
39
- add_slave_ok_flag_maybe(super, connection)
39
+ options = super
40
+ if add_slave_ok_flag?(connection)
41
+ flags = options[:flags]&.dup || []
42
+ flags << :slave_ok
43
+ options = options.merge(flags: flags)
44
+ end
45
+ options
40
46
  end
41
47
 
42
- # Adds :slave_ok flag to options based on the read preference specified
43
- # in the operation or implied by the topology that the connection's
44
- # server is a part of.
48
+ # Whether to add the :slave_ok flag to the request based on the
49
+ # read preference specified in the operation or implied by the topology
50
+ # that the connection's server is a part of.
45
51
  #
46
- # @param [ Hash ] options The options calculated so far.
47
52
  # @param [ Server::Connection ] connection The connection that the
48
53
  # operation will be executed on.
49
54
  #
50
- # @return [ Hash ] The new options.
51
- def add_slave_ok_flag_maybe(options, connection)
52
- add_flag =
53
- # https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.rst#topology-type-single
54
- if connection.description.standalone?
55
- # Read preference is never sent to standalones.
56
- false
57
- elsif connection.server.cluster.single?
58
- # In Single topology the driver forces primaryPreferred read
59
- # preference mode (via the slave_ok flag, in case of old servers)
60
- # so that the query is satisfied.
61
- true
62
- else
63
- # In replica sets and sharded clusters, read preference is passed
64
- # to the server if one is specified by the application, and there
65
- # is no default.
66
- read && read.slave_ok?
67
- end
68
-
69
- if add_flag
70
- options= options.dup
71
- (options[:flags] ||= []) << :slave_ok
55
+ # @return [ true | false ] Whether the :slave_ok flag should be added.
56
+ def add_slave_ok_flag?(connection)
57
+ # https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.rst#topology-type-single
58
+ if connection.description.standalone?
59
+ # Read preference is never sent to standalones.
60
+ false
61
+ elsif connection.server.cluster.single?
62
+ # In Single topology the driver forces primaryPreferred read
63
+ # preference mode (via the slave_ok flag, in case of old servers)
64
+ # so that the query is satisfied.
65
+ true
66
+ else
67
+ # In replica sets and sharded clusters, read preference is passed
68
+ # to the server if one is specified by the application, and there
69
+ # is no default.
70
+ read && read.slave_ok? || false
72
71
  end
73
-
74
- options
75
72
  end
76
73
 
77
74
  def command(connection)
78
75
  sel = super
79
- update_selector_for_read_pref(sel, connection)
76
+ add_read_preference_legacy(sel, connection)
80
77
  end
81
78
 
82
79
  # Adds $readPreference field to the command document.
@@ -95,14 +92,19 @@ module Mongo
95
92
  # operation will be executed on.
96
93
  #
97
94
  # @return [ Hash ] New command document to send to the server.
98
- def update_selector_for_read_pref(sel, connection)
95
+ def add_read_preference_legacy(sel, connection)
99
96
  if read && connection.description.mongos? && read_pref = read.to_mongos
100
- Mongo::Lint.validate_camel_case_read_preference(read_pref)
101
- sel = sel[:$query] ? sel : {:$query => sel}
102
- sel = sel.merge(:$readPreference => read_pref)
103
- else
104
- sel
97
+ # If the read preference contains only mode and mode is secondary
98
+ # preferred and we are sending to a pre-OP_MSG server, this read
99
+ # preference is indicated by the :slave_ok wire protocol flag
100
+ # and $readPreference command parameter isn't sent.
101
+ if read_pref != {mode: 'secondaryPreferred'}
102
+ Mongo::Lint.validate_camel_case_read_preference(read_pref)
103
+ sel = sel[:$query] ? sel : {:$query => sel}
104
+ sel = sel.merge(:$readPreference => read_pref)
105
+ end
105
106
  end
107
+ sel
106
108
  end
107
109
  end
108
110
  end
@@ -171,9 +171,10 @@ module Mongo
171
171
  elsif connection.description.mongos?
172
172
  # When server is a mongos:
173
173
  # - $readPreference is never sent when mode is 'primary'
174
- # - When mode is 'secondaryPreferred' $readPreference is only sent
175
- # when a non-mode field (i.e. tag_sets) is present
176
174
  # - Otherwise $readPreference is sent
175
+ # When mode is 'secondaryPreferred' $readPreference is currently
176
+ # required to only be sent when a non-mode field (i.e. tag_sets)
177
+ # is present, but this causes wrong behavior (DRIVERS-1642).
177
178
  if read
178
179
  doc = read.to_mongos
179
180
  if doc
@@ -43,8 +43,8 @@ module Mongo
43
43
  # Msg.new([:more_to_come], {}, { ismaster: 1 },
44
44
  # { type: 1, payload: { identifier: 'documents', sequence: [..] } })
45
45
  #
46
- # @param [ Array<Symbol> ] flags The flag bits. Current supported values
47
- # are :more_to_come and :checksum_present.
46
+ # @param [ Array<Symbol> ] flags The flag bits. Currently supported
47
+ # values are :more_to_come and :checksum_present.
48
48
  # @param [ Hash ] options The options.
49
49
  # @param [ BSON::Document, Hash ] main_document The document that will
50
50
  # become the payload type 0 section. Can contain global args as they
@@ -46,18 +46,18 @@ module Mongo
46
46
  # @example Find all user ids.
47
47
  # Query.new('xgen', 'users', {}, :fields => {:id => 1})
48
48
  #
49
- # @param database [String, Symbol] The database to query.
50
- # @param collection [String, Symbol] The collection to query.
51
- # @param selector [Hash] The query selector.
52
- # @param options [Hash] The additional query options.
49
+ # @param [ String, Symbol ] database The database to query.
50
+ # @param [ String, Symbol ] collection The collection to query.
51
+ # @param [ Hash ] selector The query selector.
52
+ # @param [ Hash ] options The additional query options.
53
53
  #
54
- # @option options :project [Hash] The projection.
55
- # @option options :skip [Integer] The number of documents to skip.
56
- # @option options :limit [Integer] The number of documents to return.
57
- # @option options :flags [Array] The flags for the query message.
58
- #
59
- # Supported flags: +:tailable_cursor+, +:slave_ok+, +:oplog_replay+,
60
- # +:no_cursor_timeout+, +:await_data+, +:exhaust+, +:partial+
54
+ # @option options [ Array<Symbol> ] :flags The flag bits.
55
+ # Currently supported values are :await_data, :exhaust,
56
+ # :no_cursor_timeout, :oplog_replay, :partial, :slave_ok,
57
+ # :tailable_cursor.
58
+ # @option options [ Integer ] :limit The number of documents to return.
59
+ # @option options [ Hash ] :project The projection.
60
+ # @option options [ Integer ] :skip The number of documents to skip.
61
61
  def initialize(database, collection, selector, options = {})
62
62
  @database = database
63
63
  @namespace = "#{database}.#{collection}"